上一篇:《地面站软件开发》-第1章 ArcGIS Maps SDK for Qt 加载天地图
1. 配置管理
Qt Creator 20.0.1
Qt 6.11.1
ArcGIS Maps SDK for Qt 300.1.0
Windows11 25H2
2. 效果

3. 前言
ArcGIS Maps SDK for Qt 300.X官方示例无法正常运行,本文基于官方例子做了修正,使之能正常运行。
4. ArcGIS官方样例地址
https://developers.arcgis.com/qt/cpp/sample-code/change-viewpoint/
5. 创建ArcGIS模板项目
详见第一章第4节。
6. 项目改造
6.1 CMakeList.txt
#-------------------------------------------------
# Copyright 2026 ESRI
#
# All rights reserved under the copyright laws of the United States
# and applicable international laws, treaties, and conventions.
#
# You may freely redistribute and use this sample code, with or
# without modification, provided you include the original copyright
# notice and use restrictions.
#
# See the Sample code usage restrictions document for further information.
#-------------------------------------------------
cmake_minimum_required(VERSION 3.16)
project(ArcGIS_Viewpoint LANGUAGES CXX)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC OFF)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(IOS)
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "16.0" CACHE STRING "Minimum iOS deployment version" FORCE)
elseif(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "Minimum macOS deployment version" FORCE)
endif()
find_package(Qt6 COMPONENTS REQUIRED Core Quick Multimedia Positioning Sensors WebSockets)
if (Qt6Core_VERSION VERSION_LESS 6.8.2)
message(FATAL_ERROR "This version of the ArcGIS Maps SDK for Qt requires at least Qt 6.8.2")
endif()
find_package(ArcGISRuntime 300.1.0 COMPONENTS REQUIRED Cpp)
set(SOURCE_FILES
main.cpp
ArcGIS_Viewpoint.cpp
ArcGIS_Viewpoint.h
qml/qml.qrc
Resources/Resources.qrc
$<$<BOOL:${WIN32}>:Win/Resources.rc>
$<$<BOOL:${APPLE}>:Mac/AppIcon.icns>)
if(ANDROID)
qt_add_executable(${PROJECT_NAME} ${SOURCE_FILES})
else()
qt_add_executable(${PROJECT_NAME} MACOSX_BUNDLE ${SOURCE_FILES})
# IOS uses static Runtimecore Framework
if(IOS)
# Workaround for the CMake version of https://bugreports.qt.io/browse/QTBUG-129651
# ArcGIS Maps SDK for Qt adds 'INSTALL_RPATH = ${FFMPEG_FRAMEWORKS}'
# which contains the path to the FFmpeg frameworks.
# This ensures that the FFmpeg frameworks are included in the application bundle.
qt_add_ios_ffmpeg_libraries(${PROJECT_NAME})
get_target_property(Qt6Core_LOCATION Qt6::Core LOCATION)
get_filename_component(Qt6_DIR ${Qt6Core_LOCATION} DIRECTORY)
set(FFMPEG_LIB_DIR "${Qt6_DIR}/../ffmpeg")
if(Qt6Core_VERSION VERSION_LESS "6.8.3")
set(FRAMEWORK "framework")
else()
set(FRAMEWORK "xcframework")
endif()
set(FFMPEG_FRAMEWORKS
"${FFMPEG_LIB_DIR}/libavcodec.${FRAMEWORK}"
"${FFMPEG_LIB_DIR}/libavformat.${FRAMEWORK}"
"${FFMPEG_LIB_DIR}/libavutil.${FRAMEWORK}"
"${FFMPEG_LIB_DIR}/libswresample.${FRAMEWORK}"
"${FFMPEG_LIB_DIR}/libswscale.${FRAMEWORK}")
set_target_properties(${PROJECT_NAME} PROPERTIES
INSTALL_RPATH "@executable_path/Frameworks;@loader_path/Frameworks"
XCODE_EMBED_FRAMEWORKS "${ArcGISRuntime_runtimecore_LIB};${FFMPEG_FRAMEWORKS}"
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY TRUE
LINK_FLAGS "-F${FFMPEG_LIB_DIR} -F${ArcGISRuntime_runtimecore_LIB_DIR} -Wl,-rpath,@executable_path -Wl,-rpath,@loader_path/Frameworks")
endif()
# On MacOSX add icon to app bundle.
set(MACOSX_BUNDLE_ICON_FILE AppIcon.icns)
set_source_files_properties(Mac/AppIcon.icns
PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
# Copy required dynamic libraries to the build folder as a post-build step.
if(DEFINED ArcGISRuntime_LIBRARIES)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${ArcGISRuntime_LIBRARIES}
$<TARGET_FILE_DIR:${PROJECT_NAME}>)
endif()
endif()
target_compile_definitions(${PROJECT_NAME}
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt6::Core
Qt6::Quick
Qt6::Multimedia
Qt6::Positioning
Qt6::Sensors
Qt6::WebSockets
ArcGISRuntime::Cpp)
# To integrate the toolkit, copy the `toolkitcpp` and the `common` subdirectories from the toolkit
# into your project's directory. Then uncomment the following lines to add it to your project.
# See https://github.com/Esri/arcgis-maps-sdk-toolkit-qt for details
# add_subdirectory(toolkitcpp)
# target_link_libraries(${PROJECT_NAME} PRIVATE libtoolkitcpp)
if(ANDROID)
set(PROJECT_DEPLOYABLE_LIBS
${ArcGISRuntime_LIBRARIES})
list(JOIN PROJECT_DEPLOYABLE_LIBS , PROJECT_DEPLOYABLE_LIBS_STRING)
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"${ArcGISRuntime_MapsSDKNative_JAR}"
"$<TARGET_FILE_DIR:${PROJECT_NAME}>/android-build-${PROJECT_NAME}/libs/MapsSDKNative.jar")
# Setup openssl by cloning the repo https://github.com/KDAB/android_openssl More info at https://doc.qt.io/qt-6/android-openssl-support.html
# QtCreator can also be used to install openssl https://doc.qt.io/qtcreator/creator-developing-android.html#specifying-android-device-settings
# Uncomment line below and point to your openssl path.
#include(<your_path_to_android_openssl>/CMakeLists.txt)
# QtCreator supports the following variables for Android, which are identical
# to qmake Android variables.
# Check http://doc.qt.io/qt-6/deployment-android.html for more information.
# Setting this property changed at Qt6 see doc below.
# https://doc.qt.io/qt-6/cmake-target-property-qt-android-extra-libs.html
set_property(TARGET ${PROJECT_NAME} PROPERTY QT_ANDROID_EXTRA_LIBS ${PROJECT_DEPLOYABLE_LIBS_STRING} ${ANDROID_EXTRA_LIBS})
get_property(EXTRA_LIBS TARGET ${PROJECT_NAME} PROPERTY QT_ANDROID_EXTRA_LIBS)
if(NOT EXTRA_LIBS MATCHES "libssl" OR NOT EXTRA_LIBS MATCHES "libcrypto")
message(WARNING "openssl libraries are missing, check the project CMakeLists.txt and set up openssl environment")
endif()
endif()
6.2 main.cpp
#include "ArcGIS_Viewpoint.h"
#include "ArcGISRuntimeEnvironment.h"
#include "MapQuickView.h"
#include <QDir>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "LicenseResult.h"
using namespace Esri::ArcGISRuntime;
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
//此处修改为自己注册的token,否则地图加载不出来
const QString accessToken = QString("");
if (accessToken.isEmpty()) {
qWarning()
<< "Use of ArcGIS location services, such as the basemap styles service, requires"
<< "you to authenticate with an ArcGIS account or set the API Key property.";
} else {
ArcGISRuntimeEnvironment::setApiKey(accessToken);
}
//此处1)新增返回值否则会报警报,2)修改为自己注册的License,可移除地图上的水印
Esri::ArcGISRuntime::LicenseResult result= ArcGISRuntimeEnvironment::setLicense("");
qmlRegisterType<MapQuickView>("Esri.ArcGIS_Viewpoint", 1, 0, "MapView");
qmlRegisterType<ArcGIS_Viewpoint>("Esri.ArcGIS_Viewpoint", 1, 0, "ArcGIS_Viewpoint");
QQmlApplicationEngine engine; engine.addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
engine.load(QUrl("qrc:/qml/main.qml"));
return QGuiApplication::exec();
}
6.3 ArcGIS_Viewpoint.h
#ifndef ARCGIS_VIEWPOINT_H
#define ARCGIS_VIEWPOINT_H
#include <QQuickItem>
namespace Esri::ArcGISRuntime {
class Map;
class MapQuickView;
}
#include <QObject>
Q_MOC_INCLUDE("MapQuickView.h")
class ArcGIS_Viewpoint : public QQuickItem
{
Q_PROPERTY(Esri::ArcGISRuntime::MapQuickView *mapView READ mapQuickView WRITE setMapQuickView NOTIFY mapQuickViewChanged)
Q_OBJECT
public:
explicit ArcGIS_Viewpoint(QQuickItem *parent = nullptr);
~ArcGIS_Viewpoint() override;
void componentComplete() override;
static void init();
Q_INVOKABLE void changeViewpoint(QString viewpoint);
signals:
void mapQuickViewChanged();
private:
Esri::ArcGISRuntime::MapQuickView *mapQuickView() const;
void setMapQuickView(Esri::ArcGISRuntime::MapQuickView *mapView);
double screenRatio() const;
Esri::ArcGISRuntime::Map *m_map = nullptr;
Esri::ArcGISRuntime::MapQuickView *m_mapView = nullptr;
int m_rotationValue = 0;
};
#endif
6.4 ArcGIS_Viewpoint.cpp
#include "ArcGIS_Viewpoint.h"
#include "Map.h"
#include "MapQuickView.h"
#include "MapTypes.h"
#include <Basemap.h>
#include "Viewpoint.h"
#include "MapTypes.h"
#include "MapViewTypes.h"
#include "Point.h"
#include <SpatialReference.h>
#include "Envelope.h"
#include <QFuture>
using namespace Esri::ArcGISRuntime;
ArcGIS_Viewpoint::ArcGIS_Viewpoint(QQuickItem *parent /* = nullptr */)
: QQuickItem(parent)
{
}
ArcGIS_Viewpoint::~ArcGIS_Viewpoint() = default;
void ArcGIS_Viewpoint::init()
{
qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView");
qmlRegisterType<ArcGIS_Viewpoint>("Esri.Samples", 1, 0, "ArcGIS_ViewpointSample");
}
MapQuickView *ArcGIS_Viewpoint::mapQuickView() const
{
return m_mapView;
}
void ArcGIS_Viewpoint::componentComplete()
{
QQuickItem::componentComplete();
Basemap* basemap = new Basemap(BasemapStyle::ArcGISImagery, this);
m_map = new Map(basemap, this);
m_mapView->setMap(m_map);
//移除品牌标识
m_mapView->setAttributionTextVisible(false);
}
void ArcGIS_Viewpoint::changeViewpoint(QString viewpoint)
{
if (viewpoint == "Center")
{
Point ptEsriHeadquarters(-117.195681, 34.056218, SpatialReference(4326));
m_mapView->setViewpointCenterAsync(ptEsriHeadquarters);
}
else if (viewpoint == "Center and scale")
{
Point ptHawaii(-157.564, 20.677, SpatialReference(4236));
m_mapView->setViewpointCenterAsync(ptHawaii, 4000000.0);
}
else if (viewpoint == "Geometry")
{
Envelope envBeijing(116.380, 39.920, 116.400, 39.940, SpatialReference(4236));
m_mapView->setViewpointGeometryAsync(envBeijing);
}
else if (viewpoint == "Geometry and padding")
{
Envelope envBeijing(116.380, 39.920, 116.400, 39.940, SpatialReference(4236));
m_mapView->setViewpointGeometryAsync(envBeijing, 200 * screenRatio());
}
else if (viewpoint == "Rotation")
{
m_rotationValue = (m_rotationValue + 45) % 360;
m_mapView->setViewpointRotationAsync(m_rotationValue);
}
else if (viewpoint == "Scale 1:5,000,000")
{
m_mapView->setViewpointScaleAsync(5000000.0);
}
else if (viewpoint == "Scale 1:10,000,000")
{
m_mapView->setViewpointScaleAsync(10000000.0);
}
else if (viewpoint == "Animation")
{
constexpr double xMin = -12338668.348591767;
constexpr double yMin = 5546908.424239618;
constexpr double xMax = -12338247.594362013;
constexpr double yMax = 5547223.989911933;
constexpr int wkid = 102100;
Viewpoint vpSpring(Envelope(xMin, yMin, xMax, yMax, SpatialReference(wkid)));
constexpr float duration = 4.0f;
m_mapView->setViewpointAsync(vpSpring, duration, AnimationCurve::EaseInOutCubic);
}
}
void ArcGIS_Viewpoint::setMapQuickView(MapQuickView *mapView)
{
m_mapView = mapView;
emit mapQuickViewChanged();
}
double ArcGIS_Viewpoint::screenRatio() const
{
const double width = static_cast<double>(m_mapView->width());
const double height = static_cast<double>(m_mapView->height());
return height > width ? width / height : height / width;
}
6.5 ArcGIS_ViewpointForm.qml
import QtQuick
import QtQuick.Controls
import Esri.ArcGIS_Viewpoint
Item {
id: changeViewpointSample
MapView {
id: view
anchors.fill: parent
focus: true
Component.onCompleted: {
forceActiveFocus();
}
}
ArcGIS_Viewpoint {
id: model
mapView: view
}
Pane {
anchors {
left: parent.left
top: parent.top
margins: 15
}
width: comboBoxViewpoint.width + leftPadding + rightPadding
height: comboBoxViewpoint.height + topPadding + bottomPadding
padding: 8
ComboBox {
id: comboBoxViewpoint
anchors.centerIn: parent
property int bestWidth: implicitWidth
width: bestWidth + rightPadding + leftPadding + 20
model: [ qsTr("Center"),
qsTr("Center and scale"),
qsTr("Geometry"),
qsTr("Geometry and padding"),
qsTr("Rotation"),
qsTr("Scale 1:5,000,000"),
qsTr("Scale 1:10,000,000"),
qsTr("Animation") ]
onCurrentTextChanged: {
model.changeViewpoint(comboBoxViewpoint.currentText);
}
onModelChanged: {
let w = bestWidth;
for (let i = 0; i < comboBoxViewpoint.model.length; ++i) {
metrics.text = comboBoxViewpoint.model[i];
w = Math.max(w, metrics.width);
}
bestWidth = w;
}
TextMetrics {
id: metrics
font: comboBoxViewpoint.font
}
}
}
}
6.6 main.qml
import QtQuick.Controls
import Esri.ArcGIS_Viewpoint
ApplicationWindow {
visible: true
width: 800
height: 600
ArcGIS_ViewpointForm {
anchors.fill: parent
}
}
7. 关键代码解读
7.1 中心点设置
Point ptEsriHeadquarters(-117.195681, 34.056218, SpatialReference(4326));
m_mapView->setViewpointCenterAsync(ptEsriHeadquarters);
7.2 中心点+比例设置
Point ptHawaii(-157.564, 20.677, SpatialReference(4236));
m_mapView->setViewpointCenterAsync(ptHawaii, 4000000.0);
7.3 图形设置
Envelope envBeijing(116.380, 39.920, 116.400, 39.940, SpatialReference(4236));
m_mapView->setViewpointGeometryAsync(envBeijing);
7.4 图形和边界设置
Envelope envBeijing(116.380, 39.920, 116.400, 39.940, SpatialReference(4236));
m_mapView->setViewpointGeometryAsync(envBeijing, 200 * screenRatio());
7.5 旋转设置
m_rotationValue = (m_rotationValue + 45) % 360;
m_mapView->setViewpointRotationAsync(m_rotationValue);
7.6 比例设置
_mapView->setViewpointScaleAsync(5000000.0);
7.7 动态跳转设置
m_mapView->setViewpointScaleAsync(10000000.0);
8. 完整项目代码
源码:https://github.com/Python5421/ArcGIS_Viewpoint
注意:运行前需要更改ArcGIS accessToken,还可设置License移除水印,或设置地图属性删除底部品牌信息。