《地面站软件开发》-第2章ArcGIS Maps SDK for Qt 改变视角

上一篇:《地面站软件开发》-第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移除水印,或设置地图属性删除底部品牌信息。

相关推荐
bug嘛我经常写1 天前
MIKE21结合ArcGIS制作随时间变化的降水文件(.dfs2)-笔记
经验分享·笔记·arcgis
paiooo2 天前
用 XML 保存 LLM 会话得到高可读性会话
arcgis
Q一件事2 天前
ArcGIS中TypeError: can‘t multiply sequence by non-int of type ‘str‘错误
前端·javascript·arcgis
萧行之3 天前
Observable Plot 源码深度解析——1 认识 Observable Plot
arcgis
ノBye~4 天前
Codex CLI使用记录
arcgis
serene944 天前
功能强大的Modbus从站模拟器使用说明
物联网·上位机·modbus协议·从站仿真器·从站模拟器·modbus开发工具
Quz6 天前
QML 快捷键篇:基础快捷键与组合键序列
qt·qml
2601_962381586 天前
ArcGIS+Python+AI赋能高标准农田建设项目审计提质增效
python·arcgis·ai·高标准农田审计·大数据审计
飞鹰@四海6 天前
Claude Code 报「与 Windows 版本不兼容」——完整排查与修复指南
arcgis