QGis的使用

1. QGis编译安装

1.1. Linux下的编译安装

(1)下载源码

QGis源码: https://download.qgis.org/downloads/
官方api查询:https://api.qgis.org/api/3.28/index.html
官方文档:https://qgis.org/en/docs/index.html#

官方文档中点击对应的版本-》building QGis from source(查看对应的编译要求)
QGis的使用_第1张图片
QGis3.16在Ubuntu18.04(bionic)下的编译要求:qt5.9
QGis的使用_第2张图片

(2)ccache

You should also setup ccache to speed up compile times:

cd /usr/local/bin
sudo ln -s /usr/bin/ccache gcc
sudo ln -s /usr/bin/ccache g++

or simply add /usr/lib/ccache to your PATH.

(3)编译pyqt5

PyQt5源码下载:http://www.riverbankcomputing.com/software/pyqt/download5
PyQt5-sip下载:https://pypi.org/project/PyQt5-sip/

编译:Ubuntu 14.04 64bit上安装python-pyqt5软件包(python 2.7)

python configure.py --sip-incdir=/usr/include/python2.7
make -j4
sudo make install

sip版本一致的问题:

sip -V

#pythoy
import sip
print(sip.SIP_VERSION_STR)

import PyQt5
print(PyQt5.sip.SIP_VERSION_STR)

问题:

问题0 :"no module named pyqt5.sip"解决
解决:PyQt5-sip在configure时须指定PyQt5.sip(不是sip的安装)
   python3 configure.py --sip-module PyQt5.sip
sip下载地址:
   https://www.riverbankcomputing.com/static/Downloads/sip/4.19.25/sip-4.19.25.tar.gz
   https://www.riverbankcomputing.com/software/sip/download/
PyQt5-sip下载地址:
   https://www.cnpython.com/pypi/pyqt5-sip/download
   PyQt5_sip-12.8.1.tar.gz下载地址

问题1 :qmake: could not exec ‘/usr/lib/x86_64-linux-gnu/qt4/bin/qmake’: No such file or directory
解决:没安装qt库,sudo apt-get install qt-sdk

问题2 :Error: Make sure you have a working sip on your PATH or use the --sip argument to explicitly specify a working sip.
解决:没安装sip,

问题3:sipAPIQtCore.h:28:17: fatal error: sip.h: 没有那个文件或目录
解决:没有指定sip.h所在路径,configure时使用 python configure.py --sip-incdir=/usr/include/python2.7

问题4:qprinter.h: No such file or directory
解决:第一步python configure.py命令生成各种Qt模块后,其中的QtWebKitWidgets模块由于Qt4和Qt5的qprinter.h所属模块的调整(Qt4存在于QtGui中,Qt5将其调整到QtPrintSupport中了),QtWebKitWidgets的Makefile中缺失了对QtPrintSupport的头文件目录引用,会导致后面编译PyQt5时无法找到qprinter.h头文件,编译失败(编译过程非常漫长),所以需要向刚生成的QtWebKitWidgets模块源文件的MakeFile文件的INCPATH中添加QtPrintSupport引用。
第一种方法:修改路径
原INCPATH为:

INCPATH = -I/opt/Qt/5.3/gcc/mkspecs/linux-g++ -I. -I. -I/usr/include/python3.4m 
-I/opt/Qt/5.3/gcc/include -I/opt/Qt/5.3/gcc/include/QtWebKitWidgets 
-I/opt/Qt/5.3/gcc/include/QtWebKit -I/opt/Qt/5.3/gcc/include/QtWidgets 
-I/opt/Qt/5.3/gcc/include/QtNetwork -I/opt/Qt/5.3/gcc/include/QtGui 
-I/opt/Qt/5.3/gcc/include/QtCore -I.

修改为:

INCPATH = -I/opt/Qt/5.3/gcc/mkspecs/linux-g++ -I. -I. -I/usr/include/python3.4m 
-I/opt/Qt/5.3/gcc/include -I/opt/Qt/5.3/gcc/include/QtWebKitWidgets 
-I/opt/Qt/5.3/gcc/include/QtWebKit -I/opt/Qt/5.3/gcc/include/QtWidgets 
-I/opt/Qt/5.3/gcc/include/QtNetwork -I/opt/Qt/5.3/gcc/include/QtPrintSupport 
-I/opt/Qt/5.3/gcc/include/QtGui -I/opt/Qt/5.3/gcc/include/QtCore -I.

第二种方法:直接在QtWebKitWidgets模块源文件的QtWebKitWidgets.pro文件中加入

QT += printsupport

接下来执行编译安装:

sudo make 
sudo make install

(4)编译qgis

QGis官方编译说明
中标麒麟下编译QGis
linux下直接cmake编译qgis

apt-get install 
bison 
ca-certificates 
ccache 
cmake 
cmake-curses-gui 
dh-python 
doxygen 
expect 
flex 
flip 
gdal-bin 
git 
graphviz 
grass-dev 
libexiv2-dev 
libexpat1-dev 
libfcgi-dev 
libgdal-dev 
libgeos-dev 
libgsl-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 
libqwt-qt5-dev 
libspatialindex-dev 
libspatialite-dev 
libsqlite3-dev 
libsqlite3-mod-spatialite 
libyaml-tiny-perl 
libzip-dev 
lighttpd 
locales 
ninja-build 
ocl-icd-opencl-dev 
opencl-headers 
pkg-config 
poppler-utils 
protobuf-compiler 
pyqt5-dev 
pyqt5-dev-tools 
pyqt5.qsci-dev 
python3-all-dev 
python3-autopep8 
python3-dateutil 
python3-dev 
python3-future 
python3-gdal 
python3-httplib2 
python3-jinja2 
python3-lxml 
python3-markupsafe 
python3-mock 
python3-nose2 
python3-owslib 
python3-plotly 
python3-psycopg2 
python3-pygments 
python3-pyproj
python3-pyqt5 
python3-pyqt5.qsci 
python3-pyqt5.qtsql 
python3-pyqt5.qtsvg 
python3-pyqt5.qtwebkit 
python3-requests 
python3-sip 
python3-sip-dev 
python3-six 
python3-termcolor 
python3-tz 
python3-yaml 
qt3d-assimpsceneimport-plugin 
qt3d-defaultgeometryloader-plugin 
qt3d-gltfsceneio-plugin 
qt3d-scene2d-plugin 
qt3d5-dev 
qt5-default 
qt5keychain-dev 
qtbase5-dev 
qtbase5-private-dev 
qtpositioning5-dev 
qttools5-dev 
qttools5-dev-tools 
saga 
spawn-fcgi 
pandoc 
xauth 
xfonts-100dpi 
xfonts-75dpi 
xfonts-base 
xfonts-scalable 
xvfb
mkdir BUILD
cd BUILD
cmake ..
make -j8
sudo make install

(5)qgis_core提示无法解析外部符号

库的原因,去掉spatialite.lib,更改为spatialite_i.lib就可以
QGis的使用_第3张图片
QGis的使用_第4张图片

1.2 CMakeList.txt解析

##############################################################
# 编译版本设置
SET(CPACK_PACKAGE_VERSION_MAJOR "2")
……

# Note the version no is Mmmpp for Major/minor/patch, 0-padded, thus '10100' for 1.1.0
MATH(EXPR QGIS_VERSION_INT "${CPACK_PACKAGE_VERSION_MAJOR}*10000+${CPACK_PACKAGE_VERSION_MINOR}*100+${CPACK_PACKAGE_VERSION_PATCH}")
MESSAGE(STATUS "QGIS version: ${COMPLETE_VERSION} ${RELEASE_NAME} (${QGIS_VERSION_INT})")

#############################################################
# CMake设置
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.6) # CMake最低要求
……

# 配置GRASS插件
FOREACH (GRASS_SEARCH_VERSION 6 7)
  ……

# 下面是各种编译选项
SET (WITH_DESKTOP TRUE CACHE BOOL "Determines whether QGIS desktop should be built")
SET (WITH_SERVER FALSE CACHE BOOL "Determines whether QGIS server should be built")
……

SET (WITH_CUSTOM_WIDGETS FALSE CACHE BOOL "Determines whether QGIS custom widgets for Qt Designer should be built")

SET (WITH_ASTYLE FALSE CACHE BOOL "If you plan to contribute you should reindent with scripts/prepare-commit.sh (using 'our' astyle)")

SET (WITH_POSTGRESQL TRUE CACHE BOOL "Determines whether POSTGRESQL support should be built")
……

SET (WITH_INTERNAL_QEXTSERIALPORT TRUE CACHE BOOL "Use internal build of Qextserialport")

SET (WITH_QSPATIALITE FALSE CACHE BOOL "Determines whether QSPATIALITE sql driver should be built")

SET (WITH_ORACLE FALSE CACHE BOOL "Determines whether Oracle support should be built")
……

# 如果你需要Python支持,关注这一块
SET (WITH_BINDINGS TRUE CACHE BOOL "Determines whether python bindings should be built")
IF (WITH_BINDINGS)
  ……
ENDIF (WITH_BINDINGS)

# Android移动端支持
IF (ANDROID)
    SET (DEFAULT_WITH_QTMOBILITY TRUE)
ELSE (ANDROID)
    SET (DEFAULT_WITH_QTMOBILITY FALSE)
ENDIF (ANDROID)
SET (WITH_QTMOBILITY ${DEFAULT_WITH_QTMOBILITY} CACHE BOOL "Determines if QtMobility related code should be build (for example internal GPS)")

# globe三维支持
SET (WITH_GLOBE FALSE CACHE BOOL "Determines whether Globe plugin should be built")
……

SET (PEDANTIC TRUE CACHE BOOL "Determines if we should compile in pedantic mode.")
SET (ENABLE_TESTS TRUE CACHE BOOL "Build unit tests?")
SET (ENABLE_COVERAGE FALSE CACHE BOOL "Perform coverage tests?")
SET (GENERATE_COVERAGE_DOCS FALSE CACHE BOOL "Generate coverage docs (requires lcov)?")

# hide this variable because building of python bindings might fail
# if set to other directory than expected
MARK_AS_ADVANCED(LIBRARY_OUTPUT_PATH)

# 这里是指定编译器类型
IF (MSVC AND CMAKE_GENERATOR MATCHES "NMake")
  # following variable is also used in qgsconfig.h
  SET (USING_NMAKE TRUE)
ENDIF (MSVC AND CMAKE_GENERATOR MATCHES "NMake")

#############################################################
# 这里是Flex和Bison

INCLUDE(Flex)

FIND_FLEX()

IF (NOT FLEX_EXECUTABLE)
  MESSAGE(FATAL_ERROR "Couldn't find Flex")
ENDIF (NOT FLEX_EXECUTABLE)

INCLUDE(Bison)

FIND_BISON()

IF (NOT BISON_EXECUTABLE)
  MESSAGE(FATAL_ERROR "Couldn't find Bison")
ENDIF (NOT BISON_EXECUTABLE)

#############################################################
# 下面就开始找依赖库了

IF(NOT WIN32 AND NOT ANDROID)
  ……

# 必须要的依赖库
FIND_PACKAGE(Proj)
FIND_PACKAGE(GEOS)
FIND_PACKAGE(GDAL)
FIND_PACKAGE(Expat REQUIRED)
FIND_PACKAGE(Spatialindex REQUIRED)
FIND_PACKAGE(Qwt REQUIRED)

IF (WITH_INTERNAL_QEXTSERIALPORT)
  SET(QEXTSERIALPORT_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src/core/gps/qextserialport)
ELSE (WITH_INTERNAL_QEXTSERIALPORT)
  FIND_PACKAGE(Qextserialport REQUIRED)
ENDIF(WITH_INTERNAL_QEXTSERIALPORT)

FIND_PACKAGE(Sqlite3)
IF (NOT SQLITE3_FOUND)
  MESSAGE (SEND_ERROR "sqlite3 dependency was not found!")
ENDIF (NOT SQLITE3_FOUND)

# 可选的依赖库
IF (WITH_POSTGRESQL)
  FIND_PACKAGE(Postgres) # PostgreSQL provider
ENDIF (WITH_POSTGRESQL)

FIND_PACKAGE(SpatiaLite REQUIRED)

# spatialite的版本处理
IF(SPATIALITE_VERSION_GE_4_0_0)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSPATIALITE_VERSION_GE_4_0_0")
ENDIF(SPATIALITE_VERSION_GE_4_0_0)
IF(SPATIALITE_VERSION_G_4_1_1)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSPATIALITE_VERSION_G_4_1_1")
ENDIF(SPATIALITE_VERSION_G_4_1_1)
IF(SPATIALITE_HAS_INIT_EX)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSPATIALITE_HAS_INIT_EX")
ENDIF(SPATIALITE_HAS_INIT_EX)

IF (NOT PROJ_FOUND OR NOT GEOS_FOUND OR NOT GDAL_FOUND)
  MESSAGE (SEND_ERROR "Some dependencies were not found! Proj: ${PROJ_FOUND}, Geos: ${GEOS_FOUND}, GDAL: ${GDAL_FOUND}")
ENDIF (NOT PROJ_FOUND OR NOT GEOS_FOUND OR NOT GDAL_FOUND)

IF (POSTGRES_FOUND)
  # following variable is used in qgsconfig.h
  SET (HAVE_POSTGRESQL TRUE)
ENDIF (POSTGRES_FOUND)

SET (WITH_QTWEBKIT TRUE CACHE INTERNAL "Enable QtWebkit support")
IF (WITH_QTWEBKIT)
  ADD_DEFINITIONS(-DWITH_QTWEBKIT)
ENDIF(WITH_QTWEBKIT)
#############################################################
# 找Qt4,如果设置了ENABLE_QT5会尝试去找Qt5,需要用Qt5编译的,关注这里的详细代码
SET(QT_MIN_VERSION 4.8.0)
SET (ENABLE_QT5 FALSE CACHE BOOL "If enabled will try to find Qt5 before looking for Qt4")
IF (ENABLE_QT5)
  ……

# 下面是模型测试
SET(ENABLE_MODELTEST FALSE CACHE BOOL "Enable QT ModelTest (not for production)")

IF (ENABLE_TESTS)
  ……

#############################################################
# C++11的支持
# enable use of c++11 features where available
# full c++11 support in clang 3.3+: http://clang.llvm.org/cxx_status.html
# for Mac, this is probably Apple LLVM 4.2 (based on LLVM 3.2svn, in XCode 4.6+)
#   or definitely Apple LLVM 5.0 (based on LLVM 3.3svn, in Xcode 5+):
#   https://gist.github.com/yamaya/2924292

IF (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
  EXECUTE_PROCESS(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION)
  IF (GCC_VERSION VERSION_GREATER 4.7 OR GCC_VERSION VERSION_EQUAL 4.7)
    SET(USE_CXX_11 TRUE)
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  ENDIF()
ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  IF ((NOT APPLE AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.2")
       OR (APPLE AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.1"))
    SET(USE_CXX_11 TRUE)
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-error=c++11-narrowing")
  ENDIF()
ELSEIF (MSVC AND MSVC_VERSION GREATER 1600)
  SET(USE_CXX_11 TRUE)
ELSE()
  SET(USE_CXX_11 FALSE)
ENDIF()

#allow override keyword if available
IF (NOT USE_CXX_11)
  ADD_DEFINITIONS("-Doverride=")
  ADD_DEFINITIONS("-Dnoexcept=")
  ADD_DEFINITIONS("-Dnullptr=0")
ENDIF()


#############################################################
# 设置警告信息可用

IF (PEDANTIC)
  MESSAGE (STATUS "Pedantic compiler settings enabled")
  IF(MSVC)
    ……

    # disable warnings
    SET(_warnings "${_warnings} /wd4100 ")  # unused formal parameters
   ……

ENDIF (PEDANTIC)

IF (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  ……

IF(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
 ……

IF (CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
  ……

IF(MSVC)
  ……

IF(ENABLE_COVERAGE)
  ……

#############################################################
# 操作系统环境指定的一些配置

IF (WIN32)
  ……

  IF (MSVC)
    ……

  IF (APPLE)
    ……

ENDIF (WIN32)

IF (ANDROID)
    ……

# 看一看这里,是配置预编译器选项需要的设置
ADD_DEFINITIONS("-DCORE_EXPORT=${DLLIMPORT}")
ADD_DEFINITIONS("-DGUI_EXPORT=${DLLIMPORT}")
ADD_DEFINITIONS("-DPYTHON_EXPORT=${DLLIMPORT}")
ADD_DEFINITIONS("-DANALYSIS_EXPORT=${DLLIMPORT}")
ADD_DEFINITIONS("-DAPP_EXPORT=${DLLIMPORT}")
ADD_DEFINITIONS("-DCUSTOMWIDGETS_EXPORT=${DLLIMPORT}")
ADD_DEFINITIONS("-DSERVER_EXPORT=${DLLIMPORT}")

#############################################################
# 用户可以指定的一些QGIS配置,主要针对安装好的应用程序
# user-changeable settings which can be used to customize
# layout of QGIS installation
# (default values are platform-specific)

SET (QGIS_BIN_SUBDIR     ${DEFAULT_BIN_SUBDIR}     CACHE STRING "Subdirectory where executables will be installed")
……


#############################################################
# Python的一些依赖

SET (ENABLE_PYTHON3 ${ENABLE_QT5} CACHE BOOL "If enabled will try to find Python 3 before looking for Python 2")
IF(ENABLE_PYTHON3)
  SET(PYTHON_VER 3 CACHE STRING "Python version")
ELSE(ENABLE_PYTHON3)
  SET(PYTHON_VER 2.7 CACHE STRING "Python version")
ENDIF(ENABLE_PYTHON3)

FIND_PACKAGE(PythonInterp ${PYTHON_VER} REQUIRED)

#############################################################
# Python bindings

IF (WITH_BINDINGS)
  ……

#############################################################
# create qgsconfig.h
# installed with app target

CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/cmake_templates/qgsconfig.h.in ${CMAKE_BINARY_DIR}/qgsconfig.h)
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR})

# Added by Jef to prevent python core and gui libs linking to other qgisCore and qgisGui libs
# that may be in the same install prefix
LINK_DIRECTORIES(${CMAKE_BINARY_DIR}/src/core ${CMAKE_BINARY_DIR}/src/gui)

#############################################################
# create qgsversion.h
IF (EXISTS ${CMAKE_SOURCE_DIR}/.git/index)
  ……

#############################################################
# 在输出目录中添加一些子文件夹

#create a variable to specify where our test data is
#so that unit tests can use TEST_DATA_DIR to locate
#the test data. See CMakeLists in test dirs for more info
#TEST_DATA_DIR is also used by QgsRenderChecker currently in core
SET (TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata")

ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(doc)
ADD_SUBDIRECTORY(images)
ADD_SUBDIRECTORY(resources)
ADD_SUBDIRECTORY(i18n)

IF (WITH_BINDINGS)
  ADD_SUBDIRECTORY(python)
ENDIF (WITH_BINDINGS)

IF (ENABLE_TESTS)
  ADD_SUBDIRECTORY(tests)
  SET (CTEST_BINARY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/bin" )
  MESSAGE (STATUS "Ctest Binary Directory set to: ${CTEST_BINARY_DIRECTORY}")
ENDIF (ENABLE_TESTS)

IF (APPLE)
  ……

INSTALL(FILES cmake/FindQGIS.cmake DESTINATION ${QGIS_DATA_DIR})

#############################################################
# Post-install commands
ADD_SUBDIRECTORY(postinstall)

#############################################################
# Uninstall stuff see: http://www.vtk.org/Wiki/CMake_FAQ
CONFIGURE_FILE(
  ……

#############################################################
# Enable packaging
……
QGis的使用_第5张图片

2. 使用

2.1 添加xyz源

能用的:

//高德
https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}
//google无标注
http://gac-geo.googlecnapps.cn/maps/vt?lyrs=s&x={x}&y={y}&z={z}
//google有标注
http://gac-geo.googlecnapps.cn/maps/vt?lyrs=s,m&gl=CN&x={x}&y={y}&z={z}
//google map
http://gac-geo.googlecnapps.cn/maps/vt/lyrs=r&x={x}&y={y}&z={z}		//替换能用的
//OSM(OpenStreetMap)
http://a.tile.openstreetmap.org/{z}/{x}/{y}.png

google的地址解析:
lyrs为瓦片类型:m-路线图,t-地形图,p-带标签的地形图,s-卫星图,y-带标签的卫星图,h-标签层(路名、地名)

Google Maps: 
https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}					//不能用
http://gac-geo.googlecnapps.cn/maps/vt/lyrs=r&x={x}&y={y}&z={z}		//替换能用的
Google Satellite: 
http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}
Google Satellite Hybrid: 
https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}
Google Terrain: 
https://mt1.google.com/vt/lyrs=t&x={x}&y={y}&z={z}
Google Roads: 
https://mt1.google.com/vt/lyrs=h&x={x}&y={y}&z={z}


OpenTopoMap
https://tile.opentopomap.org/{z}/{x}/{y}.png
OpenStreetMap
http://tile.openstreetmap.org/{z}/{x}/{y}.png
Google Hybrid
https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}
Google Satellite
https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}
Google Road
https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}
Bing Aerial
http://ecn.t3.tiles.virtualearth.net/tiles/a{q}.jpeg?g=1
高德卫星影像
https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}
高德路网
https://wprd01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=2&style=8<ype=11


//---------------------
Google_Maps: https://mt1.google.com/vt/lyrs=r&x={x}&y={y}&z={z}
Google_Terrain: https://mt1.google.com/vt/lyrs=t&x={x}&y={y}&z={z}
Google_Roads:https://mt1.google.com/vt/lyrs=h&x={x}&y={y}&z={z}
Google_Satellite: https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}
Google_Streets:https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}

cartocdn_dark_nolabel:http://basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png
cartocdn_light_nolabels:http://basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png
cartocdn_voyager_nolabels:https://basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}.png

ESRI_World_Imagery:https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}
ESRI_World_Light_Gray_Base:https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer/tile/{z}/{y}/{x}
ESRI_World_Topo_Map:https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}

memomaps_tilegen:http://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png

openstreetmap:https://tile.openstreetmap.org/{z}/{x}/{y}.png
openstreetmap_br:https://tile.openstreetmap.bzh/br/{z}/{x}/{y}.png
openstreetmap_cyclosm:https://a.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png
openstreetmap_hot:https://a.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png

stamen_terrain:http://a.tile.stamen.com/terrain/{z}/{x}/{y}.png
stamen_terrain_background:http://a.tile.stamen.com/terrain-background/{z}/{x}/{y}.png
stamen_terrain(高清):http://a.tile.stamen.com/terrain/{z}/{x}/{y}@2x.png
stamen_watercolor:https://stamen-tiles-c.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg
staman水彩图:http://a.tile.stamen.com/watercolor/{z}/{x}/{y}.jpg

thunderforest_cycle:https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=
thunderforest_pioneer:https://tile.thunderforest.com/pioneer/{z}/{x}/{y}.png?apikey=

wmflabs_bw_mapnik:http://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png

高德:https://blog.csdn.net/ldlzhy1984/article/details/81015180
https://blog.csdn.net/fredricen/article/details/77189453
高德矢量图:https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}
高德遥感图:http://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}



Mapbox底图:https://api.mapbox.com/styles/v1/mapbox/streets-v10/tiles/256/{z}/{x}/{y}?access_token=

天地图矢量图:https://t6.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=
天地图矢量注记:https://t2.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=
天地图遥感图:https://t3.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=
天地图遥感注记:https://t2.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL={x}&TILEROW={y}&TILEMATRIX={z}&tk=

WMTS:
Mapbox:https://api.mapbox.com/styles/v1/mapbox/streets-v11/wmts?access_token=

WFS:
天地图:http://gisserver.tianditu.gov.cn/TDTService/wf

2.2 画图

QGis画各种图

3. 二次开发

qt二次开发时的配置
QGIS二次开发:加载XYZ Tiles形式的瓦片地图
QGis二次开发例子

QT       += core gui xml

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = qgisdemo
TEMPLATE = app


SOURCES += main.cpp# \
    #mainwindow.cpp

#INCLUDEPATH += qgis-2.4.0
#INCLUDEPATH += qgis-2.4.0\core
#INCLUDEPATH += qgis-2.4.0\core\symbology-ng
#INCLUDEPATH += qgis-2.4.0\analysis
#INCLUDEPATH += qgis-2.4.0\gui

INCLUDEPATH += qgis-2.4.0
INCLUDEPATH += qgis-2.4.0/core
INCLUDEPATH += qgis-2.4.0/core/symbology-ng
INCLUDEPATH += qgis-2.4.0/analysis
INCLUDEPATH += qgis-2.4.0/gui


FORMS    += mainwindow.ui

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -llibqgis_analysis
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -llibqgis_analysis
else:unix:!macx: LIBS += -L$$PWD/lib/ -llibqgis_analysis

INCLUDEPATH += $$PWD/
DEPENDPATH += $$PWD/

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -llibqgis_core
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -llibqgis_core
else:unix:!macx: LIBS += -L$$PWD/lib/ -llibqgis_core

INCLUDEPATH += $$PWD/
DEPENDPATH += $$PWD/

win32:CONFIG(release, debug|release): LIBS += -L$$PWD/lib/ -llibqgis_gui
else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/lib/ -llibqgis_gui
else:unix:!macx: LIBS += -L$$PWD/lib/ -llibqgis_gui

INCLUDEPATH += $$PWD/
DEPENDPATH += $$PWD/
-----------------------------------
Qt4.8.6+mingw+Qgis2.4.0基于QGis的二次开发
https://blog.51cto.com/u_15127553/4237274
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "mouseprocess.h"
#include "paintprocess.h"
int main( int argc, char *argv[] )
{
	QgsApplication a( argc, argv ,true);
	//
	//注意这三行代码须要更改路径;
	QString myPluginsDir        ="D:/Qt/workspace/qgis-2.4.0/qgis-2.4.0/build/output/plugins";//插件路径(编译好的qgis目录下的plugins目录);
	QString myLayerPath1         ="F:/gis/data_1_3/10m_admin_0_countries.shp";//图层路径,必须设置为你电脑里面shp文件的路径,不然打不开数据;
	QString myLayerPath2         ="F:/gis/qgis_sample_data/shapefiles/airports.shp";//图层路径,必须设置为你电脑里面shp文件的路径,不然打不开数据;
	//

	QgsProviderRegistry::instance( myPluginsDir); //初始化插件的文件夹;
	QgsVectorLayer * mypLayer1 = new QgsVectorLayer( myLayerPath1, "myLayer1", "ogr" ); //初始化矢量图层;
	QgsVectorLayer * mypLayer2 = new QgsVectorLayer( myLayerPath2, "myLayer2", "ogr" ); //初始化矢量图层;
	QgsVectorLayer * mypLayer3 = new QgsVectorLayer(); //初始化矢量图层;
	mypLayer3->setRendererV2(QgsFeatureRendererV2::defaultRenderer(QGis::Point));
	QgsFeatureIterator iter = mypLayer2->getFeatures();
	QgsFeature feature;
	while(iter.nextFeature(feature))
	{
		QgsGeometry *geo = feature.geometry();
		QgsPoint point = geo->asPoint();
		//qDebug() << point.x() << point.y() << endl;
	}
	for(int index = 0; index < 100; index ++)
	{
		QgsPoint point;
		double xmin = -4.4802e+06;
		double xmax = 4.61512e+06;
		double ymin = 1.43353e+06;
		double ymax = 6.50259e+06;
		point.setX(xmin + (xmax - xmin) * ((double) qrand()) / RAND_MAX);
		point.setY(ymin + (ymax - ymin) * ((double) qrand()) / RAND_MAX);
		QgsFeature feature;
		QgsGeometry *geo = QgsGeometry::fromPoint(point);
		feature.setGeometry(geo);
		mypLayer3->addFeature(feature);
		mypLayer3->updateFeature(feature);
		qDebug() << point.x() << point.y() << endl;
	}
	QList<QgsPoint> ring;
	//-4.4802e+06 4.61512e+06 1.43353e+06 6.50259e+06
	ring.append(QgsPoint(-4.4802e+06, 1.43353e+06));
	ring.append(QgsPoint(4.61512e+06, 6.50259e+06));
	ring.append(QgsPoint(2.61512e+06, 4.50259e+06));
	//mypLayer2->addRing(ring);
	qDebug() << mypLayer2->featureCount() << endl;
	//QgsSingleSymbolRendererV2 *mypRenderer = new QgsSingleSymbolRendererV2( mypLayer->geometryType() );
	QList <QgsMapCanvasLayer> myLayerSet;
	// mypLayer->setRenderer( mypRenderer );
	//mypLayer->setRendererV2(mypRenderer);
	QgsMapLayerRegistry::instance()->addMapLayer( mypLayer1, true );
	QgsMapLayerRegistry::instance()->addMapLayer( mypLayer2, true );
	QgsMapLayerRegistry::instance()->addMapLayer( mypLayer3, true );
	//myLayerSet.append( QgsMapCanvasLayer(mypLayer3, true ) );
	myLayerSet.append( QgsMapCanvasLayer(mypLayer2, true ) );
	//myLayerSet.append( QgsMapCanvasLayer(mypLayer1, true ) );
	QgsRectangle extent = mypLayer2->extent();
	qDebug() << extent.xMinimum() << extent.xMaximum() << extent.yMinimum() << extent.yMaximum() << endl;
	QgsMapCanvas * mypMapCanvas = new QgsMapCanvas( 0, 0 );
	mypMapCanvas->setExtent(mypLayer2->extent() );
	mypMapCanvas->enableAntiAliasing( true);
	mypMapCanvas->setCanvasColor( QColor(255, 255, 255 ) );
	mypMapCanvas->freeze( false );
	mypMapCanvas->setLayerSet( myLayerSet);
	mypMapCanvas->setVisible( true );
	mypMapCanvas->refresh();
	mypMapCanvas->show();
	MouseProcess *mouseProcess = new MouseProcess();
	PaintProcess *paintProcess = new PaintProcess();
	QObject::connect(mypMapCanvas, SIGNAL(xyCoordinates(QgsPoint)), 
					 mouseProcess, SLOT(xyCoordinates(QgsPoint)));
	QObject::connect(mypMapCanvas, SIGNAL(renderComplete(QPainter*)), 
					 paintProcess, SLOT(renderComplete(QPainter*)));

	return a.exec();
}

验证QGis的代码:
QGIS二次开发2–读取qgis工程文件并显示

//pro中引入 qgis_core.lib qgis_gui.lib
#include "mainwindow.h"
#include 
int main(int argc char *argv[])
{
	QgsApplication a(argc, argv, true);
	QgsApplication::setPrefixPath("C:/OSGeo4W64/apps/qgis", true);
	QgsApplication::initQgis();
	MainWindow w;
	w.show();
	return a.exec();
}

// maiwindow.cpp
#include 
#include 
#include 

mapCanvas = new QgsMapCanvas(this);
ui->verticalLayout->addWidget(mapCanvas);

QString str = QString("d:/course/test.qgz");
QgsProject::instance()->read(str);
QVector<QgsVectorLayer*> vecLayers = QgsProject::instance()->layers<QgsVectorLayer*>();
qDebug()<<vecLayers.count();

//QList layers;
//layers.append(vecLayers[0]);
//mapCanvas->setLayers(layers);

QgsEditorWidgetRegistry a;
a.initEditors(mapCanvas);
bridge = new QgsLayerTreeMapCanvasBridge(QgsProject::instance()->layerTreeRoot(), mapCanvas, this);


4. 源码解析

4.1 源码目录

文件名 说明
ci
cmake 工程组织说明文件,主要是依赖库的配置说明
cmake_templates cmake模板文件
debian Linux操作系统所需
doc 帮助文档
!18n 翻译所需文件
images 图片资源文件
mac 苹果Mac操作系统所需
ms-windows 微软Windows操作系统所需
postinstall 软件安装完成之后执行的脚本操作
python python脚本支持
resources 各种资源、配置文件
rpm 默认配置文件
scripts 各种脚本
src 源代码,这个是我们关注的重点
tests 各种测试代码
tools 目前这里面只有一个Qt3迁移到Qt4的工具

src文件夹下的主要内容:

文件名 说明
3d 3d要用的
analysis 工程组织说明文件,主要是依赖库的配置说明
app cmake模板文件
auth Linux操作系统所需
core 帮助文档
crashhandler 翻译所需文件
crssync 图片资源文件
customwidgets 苹果Mac操作系统所需
gui 微软Windows操作系统所需
native 软件安装完成之后执行的脚本操作
plugins python脚本支持
process 各种资源、配置文件
providers 默认配置文件
python 各种脚本
quickgui 源代码,这个是我们关注的重点
server 各种测试代码
test 各种测试代码
ui 目前这里面只有一个Qt3迁移到Qt4的工具
/app/main.cpp    			主程序
/app/qgisapp.cpp			主窗体

/core/qgsmaplayer.cpp		图层类QgsMapLayer
/core/qgsmaptopixel.cpp		转像素坐标QgsMapToPixel
/core/qgstiledownloadmanager.cpp 瓦片下载管理
/core/vectortile/qgsvectortileloader.cpp		矢量瓦片加载
/core/vectortile/qgsvectortilelayer.cpp			矢量瓦片图层
/core/vectortile/qgsvectortilebasicrenderer.cpp 矢量瓦片渲染
/core/network/qgsblockingnetworkrequest.cpp

/gui/qgsbrowserdockwidget.cpp	浏览Dock窗体QgsBrowserDockWidget
/gui/qgsbrowserwidget.cpp		浏览Dock中的主窗体
/gui/qgslayertreeview.cpp		图层树控件
/gui/qgsmapcanvas.cpp			地图画布QgsMapCanvas
/gui/qgsmapcanvasitem.cpp		item类 QgsMapCanvasItem
 QgsDockWidget *mLayerTreeDock		//图层的DockWidget
 QgsStatusBar  *mStatusBar			//状态栏
//鼠标坐标的实时显示的流程(从canvas到edit)
QgsMapCanvas::mouseMoveEvent( QMouseEvent *e )
	mCursorPoint = getCoordinateTransform()->toMapCoordinates( mCanvasProperties->mouseLastXY );
    emit xyCoordinates( mCursorPoint );
QgsStatusBarCoordinatesWidget::setMapCanvas( QgsMapCanvas *mapCanvas )
	connect( mMapCanvas, &QgsMapCanvas::xyCoordinates, this, &QgsStatusBarCoordinatesWidget::showMouseCoordinates );
  	connect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsStatusBarCoordinatesWidget::showExtent );
QgsStatusBarCoordinatesWidget::showMouseCoordinates( const QgsPointXY &p )
  	mLastCoordinate = p;
  	updateCoordinateDisplay();
QgsStatusBarCoordinatesWidget::updateCoordinateDisplay()
    mLineEdit->setText( QgsCoordinateUtils::formatCoordinateForProject( QgsProject::instance(), mLastCoordinate, mMapCanvas->mapSettings().destinationCrs(),
                        static_cast< int >( mMousePrecisionDecimalPlaces ) ) );

4.2 主程序main()

位置:/src/app/main.cpp

//这是基于MainWindow的
QgisApp *qgis = new QgisApp( mypSplash, myRestorePlugins, mySkipBadLayers, mySkipVersionCheck, rootProfileFolder, profileName ); 
qgis->show();
qgis->completeInitialization();

4.3 图层操作

qgis图层操作源码

/*
 * +--------+                +------+                 +---------+
 * |  DATA  |                |  RAW |                 | DECODED |
 * |        | --> LOADER --> |      | --> DECODER --> |         | --> RENDERER
 * | SOURCE |                | TILE |                 |  TILE   |
 * +--------+                +------+                 +---------+
 *           QgsVectorTileLoader    QgsVectorTileDecoder         QgsVectorTileBasicRenderer
 * 
 * Data source is a place from where tiles are fetched from (URL for HTTP access, local
 * files, MBTiles file, GeoPackage file or others. Loader (QgsVectorTileLoader) class
 * takes care of loading data from the data source. The "raw tile" data is just a blob
 * (QByteArray) that is encoded in some way. There are multiple ways how vector tiles
 * are encoded just like there are different formats how to store images. For example,
 * tiles can be encoded using Mapbox Vector Tiles (MVT) format or in GeoJSON. Decoder
 * (QgsVectorTileDecoder) takes care of decoding raw tile data into QgsFeature objects.
 * A decoded tile is essentially an array of vector features for each sub-layer found
 * in the tile - this is what vector tile renderer (QgsVectorTileRenderer) expects
 * and does the map rendering.
 */
/core/vector/qgsvectorlayer.h   矢量图层
/core/vectortile/qgsvectortilelayer.cpp		矢量瓦片图层
QgsVectorTileLayerRenderer::render()
{

}

4.5 瓦片下载

//底层请求(最终调用)------
QgsTileDownloadManagerReply *QgsTileDownloadManager::get( const QNetworkRequest &request )
{
	QgsTileDownloadManager::QueueEntry entry = findEntryForRequest( request );
	if ( !entry.isValid() )
	{
		QgsDebugMsgLevel( QStringLiteral( "Tile download manager: get (new entry): " ) + request.url().toString(), 2 );
		// create a new entry and add it to queue
		entry.request = request;
		entry.objWorker = new QgsTileDownloadManagerReplyWorkerObject( this, request );
		entry.objWorker->moveToThread( mWorkerThread );
		
		QObject::connect( entry.objWorker, &QgsTileDownloadManagerReplyWorkerObject::finished, reply, &QgsTileDownloadManagerReply::requestFinished ); 
	}
}
QgsMapToolIdentify::identify( const QgsGeometry &geometry, IdentifyMode mode, const QList<QgsMapLayer *> &layerList, LayerType layerType, const QgsIdentifyContext &identifyContext )
{
	for ( int i = 0; i < layerCount; i++ )
    {
	    QgsMapLayer *layer = targetLayers.value( i );
	    if ( identifyLayer( &results, layer,  mLastGeometry, mLastExtent, mLastMapUnitsPerPixel, layerType, identifyContext ) )
	    {
	    	if ( mode == TopDownStopAtFirst )
	       		break;
	    }
    }
}
QgsMapToolIdentify::identifyLayer( QList<IdentifyResult> *results, QgsMapLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType, const QgsIdentifyContext &identifyContext )
{
	if ( layer->type() == QgsMapLayerType::RasterLayer && layerType.testFlag( RasterLayer ) )
	{
	  	return identifyRasterLayer( results, qobject_cast<QgsRasterLayer *>( layer ), geometry, viewExtent, mapUnitsPerPixel, identifyContext );
	}
	else if ( layer->type() == QgsMapLayerType::VectorLayer && layerType.testFlag( VectorLayer ) )
	{
	  	return identifyVectorLayer( results, qobject_cast<QgsVectorLayer *>( layer ), geometry, identifyContext );
	}
	else if ( layer->type() == QgsMapLayerType::MeshLayer && layerType.testFlag( MeshLayer ) )
	{
	  	return identifyMeshLayer( results, qobject_cast<QgsMeshLayer *>( layer ), geometry, identifyContext );
	}
	else if ( layer->type() == QgsMapLayerType::VectorTileLayer && layerType.testFlag( VectorTileLayer ) )
	{
	  	return identifyVectorTileLayer( results, qobject_cast<QgsVectorTileLayer *>( layer ), geometry, identifyContext );
	}
	else if ( layer->type() == QgsMapLayerType::PointCloudLayer && layerType.testFlag( PointCloudLayer ) )
	{
	  	return identifyPointCloudLayer( results, qobject_cast<QgsPointCloudLayer *>( layer ), geometry, identifyContext );
	}
	else
	{
	  	return false;
	}
}
QgsMapToolIdentify::identifyVectorTileLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorTileLayer *layer, const QgsGeometry &geometry, const QgsIdentifyContext &identifyContext )
{
	const int tileZoom = layer->tileMatrixSet().scaleToZoomLevel( mCanvas->scale() );
	const QgsTileMatrix tileMatrix = layer->tileMatrixSet().tileMatrix( tileZoom );
	const QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( r );
	
	for ( int row = tileRange.startRow(); row <= tileRange.endRow(); ++row )
	{
		for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); ++col )
	  	{
	    	QgsTileXYZ tileID( col, row, tileZoom );
	    	QByteArray data = layer->getRawTile( tileID );	//图层获取瓦片(见下)
	    	if ( data.isEmpty() )
	      		continue;  // failed to get data
	
	    	QgsVectorTileMVTDecoder decoder( layer->tileMatrixSet() );
	    	if ( !decoder.decode( tileID, data ) )
	      		continue;  // failed to decode
	
	   	 	QMap<QString, QgsFields> perLayerFields;
	    	const QStringList layerNames = decoder.layers();
	    	for ( const QString &layerName : layerNames )
	    	{
	      		QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
	      		perLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( fieldNames );
	    	}
	
	    	const QgsVectorTileFeatures features = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
	    	const QStringList featuresLayerNames = features.keys();
	    	for ( const QString &layerName : featuresLayerNames )
	    	{
	      		const QgsFields fFields = perLayerFields[layerName];
	      		const QVector<QgsFeature> &layerFeatures = features[layerName];
	      		for ( const QgsFeature &f : layerFeatures )
	      		{
	        		if ( f.geometry().intersects( r ) && ( !selectionGeomPrepared || selectionGeomPrepared->intersects( f.geometry().constGet() ) ) )
	        		{
	          			QMap< QString, QString > derivedAttributes = commonDerivedAttributes;
	          			derivedAttributes.insert( tr( "Feature ID" ), FID_TO_STRING( f.id() ) );
	
	          			results->append( IdentifyResult( layer, layerName, fFields, f, derivedAttributes ) );
	
	          			featureCount++;
	        		}
	      		}
	    	}
	  	}
	}
}
//图层选择
QgsVectorTileLayer::selectByGeometry( const QgsGeometry &geometry, const QgsSelectionContext &context, Qgis::SelectBehavior behavior, Qgis::SelectGeometryRelationship relationship, Qgis::SelectionFlags flags, QgsRenderContext *renderContext )
{
	for ( int row = tileRange.startRow(); row <= tileRange.endRow(); ++row )
	{
		for ( int col = tileRange.startColumn(); col <= tileRange.endColumn(); ++col )
		{
			QgsTileXYZ tileID( col, row, tileZoom );
			QByteArray data = getRawTile( tileID );
			...
		}
	}
}
//矢量图层获取瓦片
QByteArray QgsVectorTileLayer::getRawTile( QgsTileXYZ tileID )
{
  const QgsTileMatrix tileMatrix = mMatrixSet.tileMatrix( tileID.zoomLevel() );
  const QgsTileRange tileRange( tileID.column(), tileID.column(), tileID.row(), tileID.row() );

  QgsDataSourceUri dsUri;
  dsUri.setEncodedUri( mDataSource );
  const QString authcfg = dsUri.authConfigId();

  QList<QgsVectorTileRawData> rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, tileMatrix, QPointF(), tileRange, authcfg, dsUri.httpHeaders() );
  if ( rawTiles.isEmpty() )
    return QByteArray();
  return rawTiles.first().data;
}
//矢量加载器阻塞下载
QList<QgsVectorTileRawData> QgsVectorTileLoader::blockingFetchTileRawData( const QString &sourceType, const QString &sourcePath, const QgsTileMatrix &tileMatrix, const QPointF &viewCenter, const QgsTileRange &range, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback )
{
	for ( QgsTileXYZ id : std::as_const( tiles ) )
	{
		if ( feedback && feedback->isCanceled() )
			return rawTiles;
		//三种下载方式:网络下载、MB瓦片下载、Vtpk下载
		QByteArray rawData = isUrl ? loadFromNetwork( id, tileMatrix, sourcePath, authid, headers, feedback )
		                     : ( mbReader ? loadFromMBTiles( id, *mbReader ) : loadFromVtpk( id, *vtpkReader ) );
		if ( !rawData.isEmpty() )
		{
		 	rawTiles.append( QgsVectorTileRawData( id, rawData ) );
		}
	}
}
//网络下载
QByteArray QgsVectorTileLoader::loadFromNetwork( const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback )
{
	QgsBlockingNetworkRequest req;			//阻塞请求
	req.setAuthCfg( authid ); 
	QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr, false, feedback );  
	QgsNetworkReplyContent reply = req.reply(); 
	return reply.content();
}
//阻塞式网络请求
QgsBlockingNetworkRequest::get( QNetworkRequest &request, bool forceRefresh, QgsFeedback *feedback )
{
  return doRequest( Get, request, forceRefresh, feedback );
}
QgsBlockingNetworkRequest::doRequest( QgsBlockingNetworkRequest::Method method, QNetworkRequest &request, bool forceRefresh, QgsFeedback *feedback )
{
	const std::function<void()> downloaderFunction = [ this, request, &waitConditionMutex, &authRequestBufferNotEmpty, &threadFinished, &success, requestMadeFromMainThread ]()
	{
		// this function will always be run in worker threads -- either the blocking call is being made in a worker thread,
		// or the blocking call has been made from the main thread and we've fired up a new thread for this function
		Q_ASSERT( QThread::currentThread() != QgsApplication::instance()->thread() );		
		QgsNetworkAccessManager::instance( Qt::DirectConnection );		
		success = true;		
		sendRequestToNetworkAccessManager( request );	//执行请求(见下)
		
		if ( mFeedback )
		 	connect( mFeedback, &QgsFeedback::canceled, mReply, &QNetworkReply::abort );
		 			
		if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkReply( mReply, mAuthCfg ) )
		{
			mErrorCode = NetworkError;
			mErrorMessage = errorMessageFailedAuth();
			QgsMessageLog::logMessage( mErrorMessage, tr( "Network" ) );
			if ( requestMadeFromMainThread )
			 	authRequestBufferNotEmpty.wakeAll();
			success = false;
		}
		else
		{
			// We are able to use direct connection here, because we
			// * either run on the thread mReply lives in, so DirectConnection is standard and safe anyway
			// * or the owner thread of mReply is currently not doing anything because it's blocked in future.waitForFinished() (if it is the main thread)
			connect( mReply, &QNetworkReply::finished, this, &QgsBlockingNetworkRequest::replyFinished, Qt::DirectConnection );
			connect( mReply, &QNetworkReply::downloadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
			connect( mReply, &QNetworkReply::uploadProgress, this, &QgsBlockingNetworkRequest::replyProgress, Qt::DirectConnection );
			
			auto resumeMainThread = [&waitConditionMutex, &authRequestBufferNotEmpty ]()
			{
			  // when this method is called we have "produced" a single authentication request -- so the buffer is now full
			  // and it's time for the "consumer" (main thread) to do its part
			  waitConditionMutex.lock();
			  authRequestBufferNotEmpty.wakeAll();
			  waitConditionMutex.unlock();
			
			  // note that we don't need to handle waking this thread back up - that's done automatically by QgsNetworkAccessManager
			};
			
			if ( requestMadeFromMainThread )
			{
			  connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::authRequestOccurred, this, resumeMainThread, Qt::DirectConnection );
			  connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::proxyAuthenticationRequired, this, resumeMainThread, Qt::DirectConnection );
			
			#ifndef QT_NO_SSL
			  connect( QgsNetworkAccessManager::instance(), &QgsNetworkAccessManager::sslErrorsOccurred, this, resumeMainThread, Qt::DirectConnection );
			#endif
			 }
			 QEventLoop loop;
			 // connecting to aboutToQuit avoids an on-going request to remain stalled
			 // when QThreadPool::globalInstance()->waitForDone()
			 // is called at process termination
			 connect( qApp, &QCoreApplication::aboutToQuit, &loop, &QEventLoop::quit, Qt::DirectConnection );
			 connect( this, &QgsBlockingNetworkRequest::finished, &loop, &QEventLoop::quit, Qt::DirectConnection );
			 loop.exec();
		} //end of else
	};	//end of  function
}
QgsBlockingNetworkRequest::sendRequestToNetworkAccessManager( const QNetworkRequest &request )
{
	switch ( mMethod )
	{
		case Get:
		  mReply = QgsNetworkAccessManager::instance()->get( request );  //instance()回头看一下,有意思
		  break;
		
		case Post:
		  mReply = QgsNetworkAccessManager::instance()->post( request, mPayloadData );
		  break;
		
		case Head:
		  mReply = QgsNetworkAccessManager::instance()->head( request );
		  break;
		
		case Put:
		  mReply = QgsNetworkAccessManager::instance()->put( request, mPayloadData );
		  break;
		
		case Delete:
		  mReply = QgsNetworkAccessManager::instance()->deleteResource( request );
		  break;
	};
}

//矢量瓦片加载器构造时调用了异步加载
QgsVectorTileLoader::QgsVectorTileLoader( const QString &uri, const QgsTileMatrix &tileMatrix, const QgsTileRange &range, const QPointF &viewCenter, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback )
  : mEventLoop( new QEventLoop )
  , mFeedback( feedback )
  , mAuthCfg( authid )
  , mHeaders( headers )
{ 
	QVector<QgsTileXYZ> tiles = QgsVectorTileUtils::tilesInRange( range, tileMatrix.zoomLevel() );
	QgsVectorTileUtils::sortTilesByDistanceFromCenter( tiles, viewCenter );
	for ( QgsTileXYZ id : std::as_const( tiles ) )
	{
	  	loadFromNetworkAsync( id, tileMatrix, uri );
	}
}
//异步矢量下载
QgsVectorTileLoader::loadFromNetworkAsync( const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl )
{
	 QString url = QgsVectorTileUtils::formatXYZUrlTemplate( requestUrl, id, tileMatrix );
	 QNetworkRequest request( url );		//网络请求
	 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLoader" ) );
	 QgsSetRequestInitiatorId( request, id.toString() );
	
	 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 1 ), id.column() );
	 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 2 ), id.row() );
	 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 3 ), id.zoomLevel() );
	
	 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
	 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
	
	 mHeaders.updateNetworkRequest( request );
	
	 if ( !mAuthCfg.isEmpty() &&  !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
	 {
	   QgsMessageLog::logMessage( tr( "network request update failed for authentication config" ), tr( "Network" ) );
	 }
	
	 QgsTileDownloadManagerReply *reply = QgsApplication::tileDownloadManager()->get( request );	//执行请求(见下)
	 connect( reply, &QgsTileDownloadManagerReply::finished, this, &QgsVectorTileLoader::tileReplyFinished );
	 mReplies << reply;
}

//wms下载
QgsWmsTiledImageDownloadHandler::QgsWmsTiledImageDownloadHandler( )
{
	for ( const QgsWmsProvider::TileRequest &r : constRequests )
	{
		QNetworkRequest request( r.url );
		QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsWmsTiledImageDownloadHandler" ) );
		auth.setAuthorization( request );
		request.setRawHeader( "Accept", "*/*" );
		request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
		request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
		request.setAttribute( static_cast<QNetworkRequest::Attribute>( TileReqNo ), mTileReqNo );
		request.setAttribute( static_cast<QNetworkRequest::Attribute>( TileIndex ), r.index );
		request.setAttribute( static_cast<QNetworkRequest::Attribute>( TileRect ), r.rect );
		request.setAttribute( static_cast<QNetworkRequest::Attribute>( TileRetry ), 0 );
		//执行请求------
		QgsTileDownloadManagerReply *reply = QgsApplication::tileDownloadManager()->get( request );
		connect( reply, &QgsTileDownloadManagerReply::finished, this, &QgsWmsTiledImageDownloadHandler::tileReplyFinished );
		mReplies << reply;
	}
}

4.6 qgis插件框架

qgis插件框架
qgis插件开发详细教程

4.7 坐标转换

//文件位置:/core/qgsmaptopixel.h
 QgsPointXY toMapCoordinates( double x, double y ) const SIP_PYNAME( toMapCoordinatesF )
    {
      bool invertible;
      const QTransform matrix = mMatrix.inverted( &invertible );
      assert( invertible );
      qreal mx, my;
      matrix.map( static_cast< qreal >( x ), static_cast< qreal >( y ), &mx, &my );
      return QgsPointXY( mx, my );
    }

4.8 拖动

QgsMapCanvas::mousePressEvent( QMouseEvent *e )
 	if ( mMapTool->flags() & QgsMapTool::AllowZoomRect  && 
 	     e->button() == Qt::LeftButton 					&& 
 	     e->modifiers() & Qt::ShiftModifier )
    {
        beginZoomRect( e->pos() );
    }
QgsMapCanvas::mouseReleaseEvent( QMouseEvent *e )
	if ( mZoomDragging && e->button() == Qt::LeftButton )		//鼠标左键拖动地图
    	endZoomRect( e->pos() );     
QgsMapCanvas::endZoomRect( QPoint pos )
	QgsPointXY c = mSettings.mapToPixel().toMapCoordinates( mZoomRect.center() );		//中心点geo坐标
  	zoomByFactor( sf, &c );
QgsMapCanvas::zoomByFactor( double scaleFactor, const QgsPointXY *center, bool ignoreScaleLock )
	setExtent( r, true );
QgsMapCanvas::setExtent( const QgsRectangle &r, bool magnified )
	emit extentsChanged();

你可能感兴趣的:(qgis,qgis)