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

相关推荐
上海懒图科技2 天前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
非科班Java出身GISer6 天前
ArcGIS JS 基础教程(48):SceneView watch 状态监听
arcgis·arcgis js基础·arcgis js入门·arcgis js监听状态·arcgis js监听事件·arcgis js 监听飞行·arcgis js 监听功能
JudithHuang8 天前
React 嵌套路由与布局
前端·react.js·arcgis·react·react router
Dxy12393102169 天前
JS DES 加密详解
开发语言·javascript·arcgis
非科班Java出身GISer11 天前
ArcGIS JS 基础教程(46):编辑要素属性 FeatureForm
arcgis·arcgis js编辑要素·arcgis js 要素·arcgis js要素属性·arcgis js 编辑属性·arcgis js编辑要素属性
半杯咖啡半行码12 天前
QML进阶指南:状态动画与模型视图实战详解
运维·qml
半杯咖啡半行码13 天前
QML进阶实战(二):异步网络、性能优化与工程化落地
网络·qt·性能优化·qml
梅如你13 天前
ISPRS | 1990-2020年间中国人工林与天然林的空间分布数据
arcgis·gis·遥感
行走的GISer14 天前
三维场景数据接入清单:六类数据、两条来源
gis·三维可视化·倾斜摄影·3dtiles·天地图
波力海苔夹心脆67515 天前
C# 委托与事件详解:委托声明、Action/Func、多播、event 与 EventHandler 入门教程
开发语言·c#·.net·上位机·委托·事件