Recreate the C++ API sample level

Create a new project

To launch Unreal Engine, open the Epic Games Launcher and click on Unreal Engine > Launch. When you launch Unreal Engine (UE4), the Unreal Project Browser automatically opens.

  1. Under New Project Categories, select Games.

    create-new-project

  2. Select a Blank template.

    select-template

  3. On the Project Settings page, set the project to a C++ project and keep the other default options.

    select-project-settings

  4. At the bottom of the Project Settings page, select where you want to store your project, and give your project a name. Click Create Project to initialize the project.

Install the plugin

  1. Create a Plugins folder in the main project directory.

    create-plugin-folder

  2. Extract the ArcGIS Maps SDK folder from the .zip file and copy it to the new Plugins folder.

    place-folder

Initialize the API

C++ applications need at least one Header file and a Source Code file (.h and .cpp).

You can find a sample header file in:

 
1
Plugins\ArcGISMapsSDK\Source\ArcGISSamples\Public\SampleAPIMapCreator.h

And a sample C++ file in

 
1
Plugins\ArcGISMapsSDK\Source\ArcGISSamples\Private\SampleAPIMapCreator.cpp
  1. To create these scripts in your project, you need to go to the content folder, right click to bring up the context menu, then select the New C++ Class menu item.

  2. This will show the Choose Parent Class wizard. You want your script to attach to an Actor. Select the Actor parent class, then click on the Next button:

    choose-parent-class

  3. Name the Actor whatever you like and set where you like to place the Actor script. It can be set in the host project or in a different module. The Public/Private button will define whether this class will be public or private. Also, you can define the path where the file will be. After you fill out the parameters click Create Class.

    name-new-actor

  4. After Unreal Engine compiles the project, you can see a new object in your Content Browser with the name of your class. You can double click on it and Visual Studio (or your default code editor) will automatically open.

Create header file

  1. Review the header file. Add your first include after Actor.h. You will add a header file for ArcGISRendererComponent. All included header files should be included before MyActor.generated.h.

     
    1
    #include "Components/ArcGISRendererComponent.h"
  2. Add a member variable that will contain an instance of our ArcGISRenderer. You can set the variable to be either public or private, depending on your project. In our example, we made it private:

     
    1
    UArcGISRendererComponent* ArcGISRendererComponent;
  3. Create the primary method that will execute our code. In our example, we named it CreateArcGISMap() and set it to private.

     
    1
    void CreateArcGISMap();

This is the complete source code for our header file:

                          
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
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"

#include "Components/ArcGISRendererComponent.h"

#include "SampleAPIMapCreator.generated.h"

// The declaration of our Actor class
UCLASS()
class ARCGISSAMPLES_API ASampleAPIMapCreator : public AActor
{
    GENERATED_BODY()

public:
    ASampleAPIMapCreator();

protected:
    virtual void BeginPlay() override;

private:
    UArcGISRendererComponent* ArcGISRendererComponent;

    void CreateArcGISMap();
};

Create C++ file

Now we will write the C++ source code file.

  1. First, include all necessary headers.

                                
    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
    27
    28
    #include "Components/DirectionalLightComponent.h"
    #include "Components/SkyLightComponent.h"
    #include "Engine/DirectionalLight.h"
    #include "Engine/SkyLight.h"
    #include "Engine/World.h"
    #include "Kismet/GameplayStatics.h"
    
    #include "ArcGISMapsSDKLib/API/GameEngine/Camera/ArcGISCamera.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/Elevation/ArcGISImageElevationSource.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/Extent/ArcGISExtentRectangle.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/Layers/ArcGIS3DModelLayer.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/Layers/ArcGISImageLayer.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/Map/ArcGISBasemap.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/Map/ArcGISMap.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/Map/ArcGISMapElevation.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/Map/ArcGISMapType.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/View/Event/ArcGISElevationSourceViewStateChangedEvent.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/View/Event/ArcGISRendererViewStateChangedEvent.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/View/Event/ArcGISVisualLayerViewStateChangedEvent.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/View/State/ArcGISElevationSourceViewState.h"
    #include "ArcGISMapsSDKLib/API/GameEngine/View/State/ArcGISVisualLayerViewState.h"
    #include "ArcGISMapsSDKLib/API/Unreal/Collection.h"
    
    #include "Components/ArcGISDirectionalLightRepositionComponent.h"
    #include "Components/ArcGISSkyLightRepositionComponent.h"
    
    #include "Math/Convert.h"
    #include "SampleDefaultPawn.h"
  2. In the constructor, create a new Renderer Component and attach it to your main actor with this code:

          
    1
    2
    3
    4
    5
    6
    ASampleAPIMapCreator::ASampleAPIMapCreator()
    {
        PrimaryActorTick.bCanEverTick = false;
        ArcGISRendererComponent = CreateDefaultSubobject<UArcGISRendererComponent>(TEXT("ArcGISRendererComponent"));
        AddOwnedComponent(ArcGISRendererComponent);
    }
    
  3. In this example, your map should be created when the application starts; call the method CreateArcGISMap() in BeginPlay():

         
    1
    2
    3
    4
    5
    void ASampleAPIMapCreator::CreateArcGISMap()
    {
        Super::BeginPlay();
        CreateArcGISMap();
    }
    
  4. Create your main function:

        
    1
    2
    3
    4
    void ASampleAPIMapCreator::CreateArcGISMap()
    {
    
    }
    
  5. Add the following code to configure an API key.

     
    1
    constexpr auto apiKey = "YOUR_API_KEY";
  6. Set the type of map to Global and create the map document.

      
    1
    2
    auto mapType = Esri::GameEngine::Map::ArcGISMapType::Global;
    auto arcGISMap = Esri::MakeShared<Esri::GameEngine::Map::ArcGISMap>(mapType);
  7. Then add a basemap layer with a basemap url and the API key.

         
    1
    2
    3
    4
    5
    // Add a basemap
    auto arcGISBasemap = Esri::GameEngine::Map::ArcGISBasemap("https://www.arcgis.com/sharing/rest/content/items/716b600dbbac433faa4bec9220c76b3a/data", apiKey);
    
    // Set the basemap
    arcGISMap->SetBasemap(arcGISBasemap);
    
  8. Create a new ArcGISElevationLayer and set your map elevation with it.

      
    1
    2
    auto elevationLayer = Esri::GameEngine::Elevation::ArcGISImageElevationSource("https://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer", "Elevation", apiKey);
    arcGISMap->SetElevation(elevationLayer);
    
  9. Add the following code to create an image layer with the name MyLayer_1 and an opacity of 1.0f. Set the visibility to true, and pass the apiKey variable. Get the arcGISMap layer array from arcGISMap by using GetLayers() and add the layer to it.

      
    1
    2
    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);
    
  10. Repeat the same process with these layers.

    • Name: MyLayer_2

         
      1
      2
      3
      auto layer_2 = Esri::GameEngine::Layers::ArcGISImageLayer("https://tiles.arcgis.com/tiles/nGt4QxSblgDfeJn9/arcgis/rest/services/New_York_Industrial/MapServer", "MyLayer_2", 1.0f, true, apiKey);
      
      arcGISMap->GetLayers().Add(layer_2);
      
    • Name: MyLayer_4

         
      1
      2
      3
      auto layer_4 = Esri::GameEngine::Layers::ArcGISImageLayer("https://tiles.arcgis.com/tiles/4yjifSiIG17X0gW4/arcgis/rest/services/NewYorkCity_PopDensity/MapServer", "MyLayer_4", 1.0f, true, apiKey);
      
      arcGISMap->GetLayers().Add(layer_4);
      
  11. Add the following code to create a 3D layer with the name MyLayer_3.

       
    1
    2
    3
    auto layer_3 = Esri::GameEngine::Layers::ArcGIS3DModelLayer("https://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_NewYork_17/SceneServer", "MyLayer_3", 1.0f, true, apiKey);
    
    arcGISMap->GetLayers().Add(layer_3);
    
  12. If you want to remove layers that were already set in the arcGISMap, use this code:

       
    1
    2
    3
    // Remove a layer
    auto index = arcGISMap->GetLayers().IndexOf(layer_4);
    arcGISMap->GetLayers().Remove(index);
    
  13. To modify the opacity of a layer outside of the constructor, call the method myLayer.SetOpacity() and pass a float between 0 and 1.

       
    1
    2
    3
    layer_1.SetOpacity(0.9f);
    layer_2.SetOpacity(0.6f);
    layer_4.SetOpacity(0.6f);
  14. If you want to use a Map Extent, set the central position and the length of the sides. For a Rectangular Extent, set the center by passing an ArcGISGlobalCoordinatesPosition to the extent.

    Note: Extents are only available in Local mode.

       
    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);
    
  15. To set your camera data to the same data as the extent, use the ArcGISGlobalCoordinatePosition and a new ArcGISRotation. These objects will set the camera position and orientation in your ArcGIS world. You can identify your camera by a name as well.

          
    1
    2
    3
    4
    5
    6
    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);
  16. You will need the ArcGISRendererView. This is the object that will take care of your camera and your map document, so you need to set their values. After populating the ArcGISRendererView, assign it to the desired ArcGISRendererComponent. In our case it is the component that we have created in the class constructor.

         
    1
    2
    3
    4
    5
    // 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);
  17. Add the following code to add callbacks to get state changes.

                                  
    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
    27
    28
    29
    30
    // 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());
        };
    
    // Set event listeners
    rendererView->SetArcGISRendererViewStateChanged(std::move(arcGISRendererViewChanged));
    rendererView->SetArcGISVisualLayerViewStateChanged(std::move(arcGISVisualLayerViewChanged));
    rendererView->SetArcGISElevationSourceViewStateChanged(std::move(arcGISElevationSourceViewChanged));
    
    // Set the renderer view to the renderer component
    ArcGISRendererComponent->SetRendererView(rendererView);
    
  18. To move around the scene and update the map details, create an Unreal Pawn. The plugin contains a custom implementation to allow you to move with the keyboard and mouse. The arcGISPawn has a camera component attached to it, allowing you to assign your renderer view to the ArcGisCameraComponent. Finally, set the pawn’s location. In this example use the same position as the arcGISCamera.

            
    1
    2
    3
    4
    5
    6
    7
    8
    // 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()));
    
  19. The ArcGIS Maps SDK plugin uses a custom location system, where the camera is always at the world origin to avoid problems related to floating point precision. For this reason, we provide different components to correct the movement of the light and the sky in order to synchronize with the camera movement. Add an ArcGISDirectionalLightRepositionComponent and an ArcGISSkyLightRepositionComponent.

                      
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // Add reposition components to the directional and sky lights
    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();
    }
    
  20. And that's all! Now the actor is completed and ready to work. The final step is to go to the Content Browser, find your custom actor and drag and drop it into the Viewport. The Actor will be added to the world outliner.

  21. Click on Play to run the app and see your map.

This is the final result when you run the app in the editor. Use the WASD keys to move around, hold the right mouse button mouse to look around or hold the left mouse button to pan.

final-result

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