安装环境搭建
QGIS的二次开发支持Windows和Linux下进行,这里分别介绍一下环境搭建。
Windows下环境搭建
网上有详细的Windows上安装QGIS二次开发环境的说明,这里就不详细介绍了。
Ubuntu24下环境搭建
(可选)WSl安装Ubuntu 24
我是直接在Windows上安装的WSL2来开发调试的,直接在Windows上安装Ubuntu24.04即可。
wsl --install Ubuntu-24.04
安装系统依赖
参考:QGIS官方安装说明
sudo apt-get update
sudo apt-get install bison build-essential ca-certificates ccache cmake cmake-curses-gui dh-python expect flex flip gdal-bin git graphviz grass-dev libdraco-dev libexiv2-dev libexpat1-dev libfcgi-dev libgdal-dev libgeos-dev libgsl-dev libmeshoptimizer-dev libpq-dev libproj-dev libprotobuf-dev libqca-qt5-2-dev libqca-qt5-2-plugins libqscintilla2-qt5-dev libqt5opengl5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5webkit5-dev libqt5xmlpatterns5-dev libspatialindex-dev libspatialite-dev libsqlite3-dev libsqlite3-mod-spatialite libyaml-tiny-perl libzip-dev libzstd-dev lighttpd locales ninja-build nlohmann-json3-dev ocl-icd-opencl-dev opencl-headers pandoc pkgconf poppler-utils protobuf-compiler pyqt5-dev pyqt5-dev-tools pyqt5.qsci-dev python3-all-dev python3-autopep8 python3-dev python3-gdal python3-matplotlib python3-mock python3-nose2 python3-owslib python3-packaging python3-psycopg2 python3-pyqt5 python3-pyqt5.qsci python3-pyqt5.qtmultimedia python3-pyqt5.qtpositioning python3-pyqt5.qtserialport python3-pyqt5.qtsql python3-pyqt5.qtsvg python3-pyqt6.sip python3-pyqtbuild python3-termcolor python3-yaml qt3d-assimpsceneimport-plugin qt3d-defaultgeometryloader-plugin qt3d-gltfsceneio-plugin qt3d-scene2d-plugin qt3d5-dev qtbase5-dev qtbase5-private-dev qtkeychain-qt5-dev qtmultimedia5-dev qtpositioning5-dev qttools5-dev qttools5-dev-tools sip-tools spawn-fcgi xauth xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable xvfb
设置 ccache(可选,但推荐)
您还应该设置 ccache 以加快编译时间:
cd /usr/local/bin
sudo ln -s /usr/bin/ccache gcc
sudo ln -s /usr/bin/ccache g++
下载QGIS源码
mkdir ~/dev
cd ~/dev
git clone https://github.com/qgis/QGIS.git
cd QGIS
# 切换到3.44分支
git checkout release-3_44
启动编译
这里是直接安装到${HOME}/apps下,不带CMAKE_INSTALL_PREFIX设置安装目录则会安装到系统目录下需要sudo权限。
mkdir build
cd build
cmake -G Ninja -D WITH_3D=true -D CMAKE_INSTALL_PREFIX=${HOME}/apps ..
构建
ninja
ninja install # 如果是不带CMAKE_INSTALL_PREFIX设置安装目录,需要带sudo
创建Qt项目
项目结构
ProjectName/
├── CMakeLists.txt
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── resources.qrc
CMakeLists.txt 配置
下面是cmake的关键配置
# QGIS configuration
set(QGIS_DEV_PATH "$ENV{HOME}/apps")
set(QGIS_INCLUDE_DIR "${QGIS_DEV_PATH}/include/qgis")
set(QGIS_LIB_DIR "${QGIS_DEV_PATH}/lib")
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Xml PrintSupport 3DRender)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Xml PrintSupport 3DRender)
# QGIS configuration - use system installation
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(QGIS QUIET qgis)
endif()
find_library(QGIS_CORE_LIBRARY NAMES qgis_core PATHS ${QGIS_LIB_DIR} NO_DEFAULT_PATH)
find_library(QGIS_GUI_LIBRARY NAMES qgis_gui PATHS ${QGIS_LIB_DIR} NO_DEFAULT_PATH)
find_library(QGIS_APP_LIBRARY NAMES qgis_app PATHS ${QGIS_LIB_DIR} NO_DEFAULT_PATH)
find_library(QGIS_NATIVE_LIBRARY NAMES qgis_native PATHS ${QGIS_LIB_DIR} NO_DEFAULT_PATH)
find_library(QGIS_ANALYSIS_LIBRARY NAMES qgis_analysis PATHS ${QGIS_LIB_DIR} NO_DEFAULT_PATH)
find_library(QGIS_3D_LIBRARY NAMES qgis_3d PATHS ${QGIS_LIB_DIR} NO_DEFAULT_PATH)
# Check if QGIS is available
if(QGIS_CORE_LIBRARY)
message(STATUS "QGIS libraries found in system")
set(QGIS_FOUND TRUE)
# Try to find QGIS include directory
find_path(QGIS_INCLUDE_DIR qgis.h
PATHS /usr/include/qgis /usr/local/include/qgis
PATH_SUFFIXES qgis
)
if(QGIS_INCLUDE_DIR)
message(STATUS "QGIS include directory: ${QGIS_INCLUDE_DIR}")
else()
message(WARNING "QGIS include directory not found")
endif()
else()
message(WARNING "QGIS libraries not found in system - building without QGIS support")
set(QGIS_FOUND FALSE)
endif()
# Add QGIS support if available
if(QGIS_FOUND)
if(QGIS_INCLUDE_DIR)
target_include_directories(ProjectName PRIVATE ${QGIS_INCLUDE_DIR})
endif()
target_compile_definitions(ProjectName PRIVATE
_USE_MATH_DEFINES
QGIS_ENABLED
)
else()
target_compile_definitions(ProjectName PRIVATE _USE_MATH_DEFINES)
endif()
# Link Qt libraries
target_link_libraries(ProjectName PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Xml Qt${QT_VERSION_MAJOR}::PrintSupport Qt${QT_VERSION_MAJOR}::3DRender)
# Link QGIS libraries if available
if(QGIS_FOUND)
target_link_libraries(ProjectName PRIVATE
${QGIS_CORE_LIBRARY}
${QGIS_GUI_LIBRARY}
${QGIS_APP_LIBRARY}
${QGIS_NATIVE_LIBRARY}
${QGIS_ANALYSIS_LIBRARY}
${QGIS_3D_LIBRARY}
)
# Set RPATH to find QGIS libraries at runtime (system paths)
set_target_properties(ProjectName PROPERTIES
BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_RPATH_USE_LINK_PATH TRUE
)
endif()
主要代码片段
main.cpp
#include "mainwindow.h"
#include "qgsapplication.h"
int main(int argc, char *argv[]) {
// QGIS 初始化
QgsApplication a(argc, argv, true);
a.init();
a.initQgis();
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "mapcanvas/mapcanvas.h"
#include "qgsmaptoolpan.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
setWindowTitle(QStringLiteral("软件标题"));
// 自定义地图类,实现自己的地图功能
MapCanvas *mapcanvas = new MapCanvas(this);
// 地图画布设置为黑色
mapcanvas->setCanvasColor(Qt::black);
// 这一步是把MapCanvas放到窗口布局的下面,这样可以实现Qt的控件叠加显示在地图上面
ui->gridLayout->addWidget(mapcanvas, 0, 0, 1, 1);
mapcanvas->lower();
// 设置地图工具,默认自带拖拽移动和缩放,可创建新的类实现自己想要的操作逻辑
QgsMapToolPan *maptool = new QgsMapToolPan(mapcanvas);
mapcanvas->setMapTool(maptool);
}
mapcanvas.cpp
#include "mapcanvas.h"
QgsRasterLayer *MapCanvas::getInitRasterLayer(const QString &url,
const QString &name) {
QgsRasterLayer *raster = new QgsRasterLayer(url, name);
// raster->setCrs(QgsCoordinateReferenceSystem::fromEpsgId(3857)); // 可以指定投影坐标系
// 设置地图显示范围为raster图层的范围
this->setExtent(raster->extent());
// 把raster图层添加到地图画布中
auto layers = this->layers();
layers.append(raster);
this->setLayers(layers);
// 设置地图显示范围为raster图层的范围
this->zoomToFullExtent();
// 刷新地图画布
this->refresh();
return raster;
}
MapCanvas::~MapCanvas() {
if (_qgsproject) {
_qgsproject->removeAllMapLayers();
}
}
MapCanvas::MapCanvas(QWidget *parent) : QgsMapCanvas(parent) {
_qgsproject = QgsProject::instance();
// 加载openstreetmap在线地图
_street_layer = getInitRasterLayer("/path/map.tif", "street");
// _street_layer = getInitRasterLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", "street","wms"); //在线地图示例
// 把图层添加到qgsproject中
_qgsproject->addMapLayer(_street_layer);
setProject(_qgsproject);
}
坐标系转换
GPS原始数据:EPSG:4326(WGS84,经纬度) 地图展示常用:EPSG:3857(Web Mercator,平面米制坐标) 在GIS中,坐标系转换是一个非常重要的功能。QGIS提供了一个坐标转换工具,可以将一个坐标系转换为另一个坐标系。 使用示例:
QgsPointXY Crs4326To3857(const QgsPointXY &wgs84Point) {
return QgsCoordinateTransform(
QgsCoordinateReferenceSystem::fromEpsgId(4326),
QgsCoordinateReferenceSystem::fromEpsgId(3857),
QgsProject::instance()).transform(wgs84Point);
}
QgsPointXY Crs3857To4326(const QgsPointXY &webMercatorPoint) {
return QgsCoordinateTransform(
QgsCoordinateReferenceSystem::fromEpsgId(3857),
QgsCoordinateReferenceSystem::fromEpsgId(4326),
QgsProject::instance()).transform(webMercatorPoint);
}
实时获取地图缩放
在QGIS中,我们可以通过连接地图画布的scaleChanged
信号来实时获取地图的缩放级别。
使用示例:
connect(this, &QgsMapCanvas::scaleChanged, this, &MapCanvas::onScaleChanged);
在MapCanvas
类中,我们需要实现onScaleChanged
槽函数来处理缩放级别变化的事件。
void MapCanvas::onScaleChanged(double scale) {
qDebug() << "Map scale changed to:" << scale;
}
自定义地图工具
在QGIS中,我们可以通过继承QgsMapTool
类来创建自定义的地图工具。自定义地图工具可以实现自己想要的地图操作逻辑,比如测量距离、绘制多边形等。
具体可重载函数可查看qgsmaptool.h头文件。
可借鉴QGIS官方实现的一些地图工具,比如QgsMapToolPan、QgsMapToolZoom等。
使用示例:
class MyMapTool : public QgsMapTool {
public:
MyMapTool(QgsMapCanvas *canvas) : QgsMapTool(canvas) {}
void canvasPressEvent(QgsMapMouseEvent *e) override {
if (e->button() == Qt::LeftButton) {
qDebug() << "Left mouse button pressed at:" << e->mapPoint();
}
}
};
QgsVectorLayer矢量图层添加
在QGIS中,我们可以通过加载矢量图层来添加矢量数据。矢量图层可以是点、线、面等几何类型的数据集。
单一符号矢量图层使用示例:
QgsVectorLayer *vectorLayer = new QgsVectorLayer("Point?crs=EPSG:3857&field=node_count:integer&field=label:string",
"clusters", "memory");
if (!vectorLayer->isValid()) {
qDebug() << "Failed to load vector layer:" << vectorLayer->errorMessage();
} else {
QgsProject::instance()->addMapLayer(vectorLayer);
}
// 创建单一符号渲染器
std::unique_ptr<QgsMarkerSymbol> symbol =
QgsMarkerSymbol::createSimple(properties);
QgsSingleSymbolRenderer *renderer =
new QgsSingleSymbolRenderer(symbol.release());
vectorLayer->setRenderer(renderer);
// 设置标签显示节点数量
QgsPalLayerSettings labelSettings;
labelSettings.fieldName = "label";
labelSettings.placement = Qgis::LabelPlacement::OverPoint;
// labelSettings.yOffset = 0; // 标签显示Y轴偏移量,默认0
labelSettings.offsetUnits = Qgis::RenderUnit::Points;
// 设置文本格式
QgsTextFormat textFormat;
textFormat.setSize(20); // 比普通节点文字更大
textFormat.setSizeUnit(Qgis::RenderUnit::Points);
textFormat.setColor(QColor(255, 0, 0)); // 黑色文字
// 设置文本缓冲区
QgsTextBufferSettings bufferSettings;
bufferSettings.setEnabled(true);
bufferSettings.setSize(1);
textFormat.setBuffer(bufferSettings);
labelSettings.setFormat(textFormat);
// 应用标签设置
QgsVectorLayerSimpleLabeling *labeling =
new QgsVectorLayerSimpleLabeling(labelSettings);
vectorLayer->setLabeling(labeling);
QGIS 3D地图显示
QGIS 3.0及以上版本支持3D地图显示。我们可以在QGIS中加载3D模型图层,实现3D地图展示。
使用示例:
初始化3D地图资源
Qgs3D::initialize();
使用示例,这里是设置为跟2D地图一样的视野缩放
// 初始化3D地图画布
Qgs3DMapCanvas *mapcanvas_3d = new Qgs3DMapCanvas();
Qgs3DMapSettings *map = new Qgs3DMapSettings();
// 获取2D地图的当前视图范围,而不是整个项目范围
QgsRectangle currentExtent = mapcanvas->projectExtent();
QgsPointXY currentCenter = mapcanvas->center();
double currentScale = mapcanvas->scale();
// 设置3D地图的视图范围和中心
map->setExtent(currentExtent);
map->setCenter(currentCenter);
map->setScale(currentScale);
// 设置3D地图的选择颜色和背景颜色
map->setSelectionColor(mapcanvas->selectionColor());
map->setBackgroundColor(QColor(0, 255, 0)); // 设置为浅灰色背景
// 设置3D地图的投影坐标系
map->setCrs(QgsCoordinateReferenceSystem::fromEpsgId(3857));
// 检查图层信息
QList<QgsMapLayer *> layers = mapcanvas->layers();
QList<QgsMapLayer *> validLayers; // 3D地图要添加的图层
for (int i = 0; i < layers.size(); ++i) {
QgsMapLayer *layer = layers[i];
if (layer) {
// 使用动态类型转换检查是否为栅格图层
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>(layer);
if (rasterLayer) {
// 3D地图只添加QgsRasterLayer
validLayers.append(rasterLayer);
}
} else {
qDebug() << "图层" << i << ":空指针";
}
}
map->setLayers(validLayers);
// 设置3D地图的时间范围
map->setTemporalRange(mapcanvas->temporalRange());
// 启用平面地形生成器
QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator;
flatTerrain->setCrs(map->crs(), prj->transformContext());
// 检查范围有效性并设置
if (currentExtent.isEmpty() || currentExtent.isNull()) {
qDebug() << "警告:当前范围无效,尝试使用默认范围";
// 使用一个默认的范围(武汉周边区域)
QgsRectangle defaultExtent(12700000, 3550000, 12750000,
3600000); // Web Mercator坐标
flatTerrain->setExtent(defaultExtent);
qDebug() << "使用默认范围:" << defaultExtent.toString();
} else {
flatTerrain->setExtent(currentExtent);
qDebug() << "地形生成器范围设置为:" << currentExtent.toString();
}
map->setTerrainGenerator(flatTerrain);
// 配置地形材质
// QgsPhongMaterialSettings terrainMaterial;
// terrainMaterial.setAmbient(QColor(120, 120, 120)); // 增强环境光颜色
// terrainMaterial.setDiffuse(QColor(220, 220, 220)); // 增强漫反射颜色
// terrainMaterial.setSpecular(QColor(255, 255, 255)); // 镜面反射颜色
// terrainMaterial.setShininess(50.0f); // 增加光泽度
// map->setTerrainShadingMaterial(terrainMaterial);
// map->setTerrainShadingEnabled(true);
// 设置光照 - 增强光照效果
// QgsPointLightSettings *pointLight = new QgsPointLightSettings;
// pointLight->setPosition(QgsVector3D(0, 0, 2000)); // 提高光源高度
// pointLight->setColor(QColor(255, 255, 255));
// pointLight->setIntensity(1.0); // 增强点光源强度
// QgsDirectionalLightSettings *dirLight = new QgsDirectionalLightSettings;
// dirLight->setDirection(QgsVector3D(-0.3, -0.3, -1.0)); // 调整光照角度
// dirLight->setColor(QColor(255, 255, 255));
// dirLight->setIntensity(1.0); // 增强方向光强度
// // 添加额外的环境光源
// QgsPointLightSettings *ambientLight = new QgsPointLightSettings;
// ambientLight->setPosition(QgsVector3D(1000, 1000, 1500));
// ambientLight->setColor(QColor(255, 255, 200)); // 暖色调环境光
// ambientLight->setIntensity(1.0);
// QList<QgsLightSource *> lightSources;
// lightSources.append(pointLight);
// lightSources.append(dirLight);
// lightSources.append(ambientLight); // 添加环境光源
// map->setLightSources(lightSources);
// 设置相机导航模式 - 使用Qgis::NavigationMode枚举
// mapcanvas_3d->cameraController()->setCameraNavigationMode(Qgis::NavigationMode::TerrainBased);
// 设置相机移动速度
map->setCameraMovementSpeed(
settings
.value(QStringLiteral("map3d/defaultMovementSpeed"), 8,
QgsSettings::App)
.toDouble());
// 设置投影类型和视野
const Qt3DRender::QCameraLens::ProjectionType defaultProjection =
settings.enumValue(QStringLiteral("map3d/defaultProjection"),
Qt3DRender::QCameraLens::PerspectiveProjection,
QgsSettings::App);
map->setProjectionType(defaultProjection);
map->setFieldOfView(settings
.value(QStringLiteral("map3d/defaultFieldOfView"), 45,
QgsSettings::App)
.toInt());
// 设置变换上下文和路径解析器
map->setTransformContext(prj->transformContext());
map->setPathResolver(prj->pathResolver());
map->setMapThemeCollection(prj->mapThemeCollection());
connect(prj, &QgsProject::transformContextChanged, map, [map] {
map->setTransformContext(QgsProject::instance()->transformContext());
});
// 3D地图设置使能
mapcanvas_3d->setMapSettings(map);
// 显示3D地图。PS.这里没有给设置parent,单独弹窗显示,实际使用需要进行管理
mapcanvas_3d->show();
加载3D模型图层
Qgs3D::add3DModelLayer(url, name);
3D地图也有Qgs3DMapTool,用于处理3D地图的交互操作,如相机移动、旋转、缩放等。 可以通过mapcanvas_3d->setMapTool()设置当前的交互工具。
矢量图层添加3D地图显示效果
// 为矢量图层添加3D显示效果
QgsVectorLayer3DRenderer *PointRule_Node_3D() {
QgsSymbolLayerList svg_list;
QgsSimpleMarkerSymbolLayer *sim = new QgsSimpleMarkerSymbolLayer();
QgsSvgMarkerSymbolLayer *svgmarker =
new QgsSvgMarkerSymbolLayer(QString(":/new/prefix1/resource/Node.svg"));
svg_list.append(svgmarker);
// svg_list.append(sim);
QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol(svg_list);
QgsPoint3DSymbol *point3DSymbol = new QgsPoint3DSymbol();
point3DSymbol->setBillboardSymbol(markerSymbol);
point3DSymbol->setShape(Qgis::Point3DShape::Billboard);
return (new QgsVectorLayer3DRenderer(point3DSymbol));
}
// 然后将渲染器设置给矢量图层
QgsAbstract3DRenderer *renderer3D = PointRule_Node_3D();
vectorLayer->setRenderer3D(renderer3D);