Use the C++ API

Learn how to use the C++ API to use ArcGIS data and other geospatial content within a scene.

Use the C++ API

To start using the API, create a new C++ class by clicking the Add New button inside the Content Browser.

In the Choose Parent Class choose Actor, which is the most basic type of object that can be placed in the world.

The new script will be created in whichever folder you have selected in the Content Broswer and Unreal Engine automatically opens Visual Stud. By default, Unreal Engine uses Visual Studio, but you can select any editor you like.

Add your newly created Actor into the level by manually selecting, dragging, and dropping the Actor from the Content Browser into the Level Viewport. This will add the Actor to the World Outliner.

Prerequisites

Review the first three steps from the Get started page to ensure your development environment is set up correctly.

Select global or local

When working with 3D content, you can choose to display your data within one of two different scene environments: global or local. The best environment for a global or local scene depends on the spatial reference of your data, the layer types, and what you are trying to achieve in your scene.

Global scene

A global scene is a view mode where you can display your 2D and 3D content on a sphere based on either the WGS84 or Web Mercator (Auxiliary Sphere) coordinate systems. A global scene is good for when you want to understand or provide context for phenomena that wraps around the spherical surface of the earth, such as global weather measurements, airline traffic paths, or shipping lanes.

To set up a global scene add the following function to the class:

  
1
2
auto mapType = Esri::GameEngine::Map::ArcGISMapType::Global;
auto arcGISMap = Esri::MakeShared<Esri::GameEngine::Map::ArcGISMap>(mapType);

Local scene

A local scene is a view mode that projects the terrain and layers on a planar surface rather than on a sphere and is only projected with the Web Mercator (Auxiliary Sphere) coordinate system. A local scene can be used to represent the entire world, and it has the option of using a fixed extent that can be clipped to the dimensions of your layers. Local scenes can be used for displaying or analyzing data at the local or city scale and are valuable for urban planning and visualizations when you want to view defined areas such as campus facilities or building developments.

To set up a local scene add:

  
1
2
auto mapType = Esri::GameEngine::Map::ArcGISMapType::Local;
auto arcGISMap = Esri::MakeShared<Esri::GameEngine::Map::ArcGISMap>(mapType);

Configure an API Key

An API key allows access to services, web maps, and web scenes hosted in ArcGIS Online while you are developing your own application. The apikey must be used in the constructor of the following elements: ArcGISLayers, ArcGISBasemap and ArcGISElevationSource.

To set up an API key:

    
1
2
3
4
constexpr auto apiKey = "Your API Key";
auto layer_1 = Esri::GameEngine::Layers::ArcGISImageLayer("https://tiles.arcgis.com/tiles/nGt4QxSblgDfeJn9/arcgis/rest/services/UrbanObservatory_NYC_TransitFrequency/MapServer", "MyLayer_1", 1.0f, true, apiKey);

arcGISMap->GetLayers().Add(layer_1);

Select basemap

A basemap provides a background of geographical context for the content in your scene. ArcGIS Maps SDK for Unity includes a basemap gallery with a variety of choices, including topography, imagery, and streets.

Add the following code to set a basemap:

   
1
2
3
auto arcGISBasemap = Esri::GameEngine::Map::ArcGISBasemap("https://www.arcgis.com/sharing/rest/content/items/716b600dbbac433faa4bec9220c76b3a/data", apiKey);

arcGISMap->SetBasemap(arcGISBasemap);

Create elevation

Elevation layers can help with 3D visualizations by creating relief in your 3D scene. Elevation surfaces define height values across the extent of a map or scene. The most common use for elevation surfaces is to define the elevation source for rasterized content and on-ground vector symbols, but surfaces are also used to define heights when editing features.

Add the following code to set an elevation:

   
1
2
3
auto elevationLayer = Esri::GameEngine::Elevation::ArcGISImageElevationSource("https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer", "Elevation", apiKey);

arcGISMap->SetElevation(elevationLayer);

Add data

Layers are the contents of a map. They include a wide range of topics about people, earth, life, and so on, and are composed of imagery, web services and local data.

What layers can you add?

  • Web Services: Raster Tile Layers, Integrated Mesh Scenes (v1.7), 3D Object Scene Layers (v1.7) and Elevation Layers
  • Local Services: Integrated Mesh and 3D Object SLPK files and TPK files (Imagery and elevation)

Add the following code to create an image layer with the layer name MyImageLayer and the layer opacity set to fully opaque:

   
1
2
3
auto layer_1 = Esri::GameEngine::Layers::ArcGISImageLayer("https://tiles.arcgis.com/tiles/nGt4QxSblgDfeJn9/arcgis/rest/services/UrbanObservatory_NYC_TransitFrequency/MapServer", "MyImageLayer", 1.0f, true, apiKey);

arcGISMap->GetLayers().Add(layer_1);

Add the following code to create a 3D layer with the name My3DLayer and the layer opacity set to fully opaque:

   
1
2
3
auto layer_2 = Esri::GameEngine::Layers::ArcGIS3DModelLayer("https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_NewYork_17/SceneServer", "My3DLayer", 1.0f, true, apiKey);

arcGISMap->GetLayers().Add(layer_2);

Add the following code to create a 3D layer from a local file with the name MyLocal3DLayer and the layer opacity set to fully opaque:

   
1
2
3
auto layer_3 = Esri::GameEngine::Layers::ArcGIS3DModelLayer("C:/Users/my_username/Downloads/example.slpk", "MyLocal3DLayer", 1.0f, true, apiKey);

arcGISMap->GetLayers().Add(layer_3);

To change the opacity of a layer:

 
1
layer_1.SetOpacity(0.9f);

The layer’s opacity takes a float that can be set between 0 and 1.

To remove a layer:

  
1
2
auto index = arcGISMap->GetLayers().IndexOf(layer_1);
arcGISMap->GetLayers().Remove(index);

Camera

In order to move around the level, you can use the ArcGIS Default Pawn.

The Pawn class is the base class of all Actors that can be controlled by players or AI. A Pawn is the physical representation of a player or AI entity within the world. This means the Pawn determines what the player or AI entity looks like visually, and it also determines how it interacts with the world through physics and collision detection.

By default, there is a one-to-one relationship between Controllers and Pawns. In other words, each Controller owns only one Pawn at any given time. Also, Pawns spawned during gameplay are not automatically possessed by a Controller.

For more information on how to manage the pawn and the camera see Pawn and Camera.

To set the initial point of view of the camera:

                         
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
String name("Camera_1");
Esri::GameEngine::Location::ArcGISRotation orientation(68, 0, 65);
Esri::GameEngine::Location::ArcGISGlobalCoordinatesPosition position(40.691242, -74.054921, 3000);

// Create a camera
auto arcGISCamera = Esri::MakeShared<Esri::GameEngine::Camera::ArcGISCamera>(name, position, orientation);

// Create the renderer view options config struct
Esri::GameEngine::View::ArcGISRendererViewOptions rendererViewOptions{true};

// Create the renderer view with camera, map and options
auto rendererView = Esri::MakeShared<Esri::GameEngine::View::ArcGISRendererView>(arcGISMap, arcGISCamera, rendererViewOptions);

// Set the renderer view to the renderer component
ArcGISRendererComponent->SetRendererView(rendererView);

// Create our sample pawn
ASampleDefaultPawn* arcGISPawn = GetWorld()->SpawnActor<ASampleDefaultPawn>();

// ASSIGN RENDERERVIEW TO THE CURRENT MAIN CAMERA COMPONENT
auto cameraComponent = arcGISPawn->FindComponentByClass<UArcGISCameraComponent>();
cameraComponent->RendererView = rendererView;

arcGISPawn->SetActorLocationAndRotation(Convert::ToFVector(rendererView->GetCameraLocalPosition()),
Convert::ToFQuat(rendererView->GetCameraLocalRotation()));

The camera can be positioned with their own coordinates through these properties:

      
1
2
3
4
5
6
arcGISCamera.Latitude = 40.691242;
arcGISCamera.Longitude = -74.054921;
arcGISCamera.Height = 3000;
arcGISCamera.Heading = 65;
arcGISCamera.Pitch =68
arcGISCamera.Roll = 0;

Create extent

In a local scene, you can clip the basemap and layers to the custom extent of your view. This is useful for increasing performance, focusing key elements of your project, and interacting with layers that are underground.

The map extent is defined by a series of bounding coordinates that delineate the area of the map or scene with which you want to work.

Set up a rectangular extent

   
1
2
3
auto extentCenter = Esri::GameEngine::Location::ArcGISGlobalCoordinatesPosition(40.691242, -74.054921, 3000);
auto extent = Esri::GameEngine::Extent::ArcGISExtentRectangle(extentCenter, 10000, 10000);
arcGISMap->SetClippingArea(extent);

Reposition direct light and the sky

Both directional light (sun) and sky must be repositioned so they can move with the map:

                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
auto directionalLightActor = UGameplayStatics::GetActorOfClass(GetWorld(), ADirectionalLight::StaticClass());
auto skyLightActor = UGameplayStatics::GetActorOfClass(GetWorld(), ASkyLight::StaticClass());

if (directionalLightActor)
{
    auto directionalLightReposition = NewObject<UArcGISDirectionalLightRepositionComponent>(directionalLightActor, UArcGISDirectionalLightRepositionComponent::StaticClass(), NAME_None, RF_Transient);
    directionalLightReposition->RendererView = rendererView;
    directionalLightReposition->RegisterComponent();
}

if (skyLightActor)
{
    auto lightReposition = NewObject<UArcGISSkyLightRepositionComponent>(skyLightActor, UArcGISSkyLightRepositionComponent::StaticClass(), NAME_None, RF_Transient);
    lightReposition->RendererView = rendererView;
    lightReposition->RegisterComponent();
}

See view state report

You can link your code to plugin public events to see changes from layers, elevation sources, and the view itself during runtime. This state will provide a combined status with a bitwise mask and will provide the error if you need it. To attach the event and log the state on the Output Log:

                          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    // Adding callbacks to get state changes from view
    Esri::GameEngine::View::Event::ArcGISRendererViewStateChangedEvent arcGISRendererViewChanged =
        [](Esri::GameEngine::View::State::ArcGISRendererViewState& state) {
            UE_LOG(LogTemp, Warning, TEXT("ArcGISRendererView status changed : %d"), (int)state.GetStatus());
        };

    // Adding callbacks to get state changes from view of ArcGISVisualLayer

    Esri::GameEngine::View::Event::ArcGISVisualLayerViewStateChangedEvent arcGISVisualLayerViewChanged =
        [](Esri::GameEngine::Layers::Base::ArcGISVisualLayer& layer, Esri::GameEngine::View::State::ArcGISVisualLayerViewState& state) {
            UE_LOG(LogTemp, Warning, TEXT("ArcGISVisualLayer status changed : %s %d"), *(FString)UTF8_TO_TCHAR(layer.GetName().data()),
                   (int)state.GetStatus());
        };

    // Adding callbacks to get state changes from view of ArcGISElevationSource

    Esri::GameEngine::View::Event::ArcGISElevationSourceViewStateChangedEvent arcGISElevationSourceViewChanged =
        [](Esri::GameEngine::Elevation::Base::ArcGISElevationSource& elevation,
           Esri::GameEngine::View::State::ArcGISElevationSourceViewState& state) {
            UE_LOG(LogTemp, Warning, TEXT("ArcGISElevationSource status changed : %s %d"), *(FString)UTF8_TO_TCHAR(elevation.GetName().data()),
                   (int)state.GetStatus());
        };

    rendererView->SetArcGISRendererViewStateChanged(std::move(arcGISRendererViewChanged));
    rendererView->SetArcGISVisualLayerViewStateChanged(std::move(arcGISVisualLayerViewChanged));
    rendererView->SetArcGISElevationSourceViewStateChanged(std::move(arcGISElevationSourceViewChanged));

If it's an error, you can call state.GetStatus() and obtain more information.

Configure OAuth 2.0

Your app may need to access items that are only shared with authorized users. For example, your organization may host private data layers that are only accessible to verified users. You can use an ArcGIS identity to allow individual users to authorize your app to use the content and services to which the user has been granted access by using the Details Panel UI or the C++ API.

To set up an OAuth Configuration:

                      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void ASampleCppOAuth::BeginPlay()
{
    Super::BeginPlay();

#if PLATFORM_ANDROID || PLATFORM_IOS
    auto arcGISOAuthAuthenticationChallengeHandler = ::MakeShared<Esri::ArcGISMapsSDK::Security::FMobileOAuthAuthenticationChallengeHandler>();
#else
    auto arcGISOAuthAuthenticationChallengeHandler = ::MakeShared<Esri::ArcGISMapsSDK::Security::FDesktopOAuthAuthenticationChallengeHandler>();
#endif

    Esri::ArcGISMapsSDK::Security::AuthenticationChallengeManager::SetOAuthChallengeHandler(arcGISOAuthAuthenticationChallengeHandler);

    Esri::GameEngine::Security::ArcGISAuthenticationManager::GetAuthenticationConfigurations().RemoveAll();

    Esri::GameEngine::Security::ArcGISOAuthAuthenticationConfiguration authenticationConfiguration;

    // ArcGIS Identity
    authenticationConfiguration =
        Esri::GameEngine::Security::ArcGISOAuthAuthenticationConfiguration(TCHAR_TO_ANSI(*ClientID), "", TCHAR_TO_ANSI(*RedirectURI));

    Esri::GameEngine::Security::ArcGISAuthenticationManager::GetAuthenticationConfigurations().Insert(TCHAR_TO_ANSI(*ServiceURL),
                                                                                                      authenticationConfiguration);

To create and set an OAuth-protected image layer:

            
1
2
3
4
5
6
7
8
9
10
11
12
// Create and set an OAuth-protected image layer
auto imageLayer = std::make_shared<Esri::GameEngine::Layers::ArcGISImageLayer>(TCHAR_TO_ANSI(*ServiceURL), "MyLayer_1", 1.0f, true, "");
arcGISMap->GetLayers().Add(*imageLayer);

imageLayer->SetLoadStatusChanged([imageLayer](void* userData, Esri::ArcGISRuntime::LoadStatus loadStatus) {
    if (loadStatus == Esri::ArcGISRuntime::LoadStatus::FailedToLoad)
    {
        auto loadError = imageLayer->GetLoadError();

        UE_LOG(LogTemp, Error, TEXT("Failed to load the ArcGISImageLayer: %s"), ANSI_TO_TCHAR(loadError.what()));
    }
});

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.