ArcGIS Blog

Developers

ArcGIS Maps SDK for .NET

Track a Moving Dynamic Entity with the ArcGIS Maps SDKs for Native Apps

By Greg DeStigter

Introduction

Integrating real-time feeds with moving track data into your Native Maps SDKs app can create engaging, interactive maps and scenes. This allows you to view real-time updates of multiple entities, helping you understand the broader situation. However, there are times when you need to focus on a single entity and track its movement on the map. Unfortunately, there isn’t a direct solution for automatically panning the map or scene to follow a dynamic entity, so the code can be challenging to write.

This article explores techniques for interacting with and tracking moving dynamic entities with the Native Maps SDKs. By the end of this article, you’ll better understand how to implement dynamic entity tracking in your real-time applications.

  1. Sample Application
  2. Following a DynamicEntity
    1. Manually Pan the GeoView
    2. Custom LocationDataSource with MapView.LocationDisplay
    3. SceneView and CameraController
  3. Summary

The Dynamics: DynamicEntity, DynamicEntityObservation, and DynamicEntityDataSource

Several important classes in the ArcGIS Maps SDKs for Native Apps help manage and track moving entities. When developing a real-world application where live entities are always on the move, it’s essential to understand these core “dynamic” classes: DynamicEntityDynamicEntityObservation, and DynamicEntityDataSource.

DynamicEntity represents a real-world object, like a vehicle, person, or asset, whose location and attributes can change over time. In real-time tracking applications, dynamic entities are usually the central elements. Each DynamicEntity has historical snapshots, called observations, represented by DynamicEntityObservation objects. An observation captures an entity’s state, including its location and attributes, at a specific point in time. The Native Maps SDK reads these observations from a data stream provided by a DynamicEntityDataSource object, which periodically updates its associated entities with new observations.

This article explores these “dynamic” objects and how they’re used to simulate and track moving entities.

Sample Application

You can find a sample application that demonstrates the techniques described in this article on my GitHub repo. Source code from this application is referred to in later sections of this article and can be used as a reference for setting up similar code in your apps.

Sample application demonstrating how to follow a dynamic entity.
Follow Entity Sample Application

This app simulates deliveries for a fictional warehousing company in San Diego, with warehouses scattered around the city. It simulates deliveries from each warehouse to random points throughout the city. Delivery trucks are represented by DynamicEntity objects, and their locations are updated periodically using a custom DynamicEntityDataSource. To make the simulation more realistic, our custom data source uses an offline route task to create valid driving routes from the warehouse to the delivery point and back. The app lets the user select a truck on the map and follow its movement around the city.

This sample app leverages the ArcGIS Maps SDK for .NET, though similar applications can be developed using any platform supported by the ArcGIS Maps SDKs for Native Apps. See the developer guide for information about dynamic entities in the Native Maps SDKs: Kotlin | .NET | Qt | Swift.

ArcGIS Maps SDK Toolkit

Check it out!

Custom Data Sources

The DynamicEntityDataSource class in the ArcGIS Maps SDKs for Native Apps serves as the base class for real-time streaming data sources in the Esri platform, such as ArcGISStreamService. But user code can also extend it to create custom data sources. The sample app used in this article, for instance, creates a custom DynamicEntityDataSource to simulate delivery truck progress. It generates driving routes and emits a new observation for each truck at a configurable time interval, including its simulated location. See the corresponding code in the SimulationSource class from the sample repo. Creating a custom data source like this is extremely useful for testing or demoing an app, as it eliminates the need to connect to a live service.

For more information, you can refer to an excellent blog post on creating custom data sources: Craft your own dynamic entity data source for ArcGIS Maps SDKs for Native Apps.

Following a Single DynamicEntity

For our application to follow a single DynamicEntity, the viewpoint of the MapView or SceneView needs to be updated to ensure the entity remains visible as it moves. Let’s look at some techniques to achieve this, highlighting their advantages, disadvantages, and when to apply them in an application.

Selecting a DynamicEntity

Our sample app uses a selection halo to indicate which truck the user is interested in. To select an entity from the data source, the user taps on a truck symbol on the map. The GeoView.IdentifyLayerAsync method is used to find the DynamicEntityObservation associated with the user’s tap point. You’ll see in the code snippet below, that IdentifyLayerAsync does not return DynamicEntity objects but rather returns individual observations. This is helpful in our case as the user may tap on any observation in the track of the delivery truck they’re interested in. This alleviates the difficulty of tapping on a moving object as the latest observation in the track may be changing quickly and tapping on a previous static observation is easier for the user. Once we have an observation in the track, we get its associated DynamicEntity and use the DynamicEntityLayer.SelectDynamicEntity method to select the entity on the map. When a DynamicEntity is selected, the selection halo moves with the entity, so whenever a new observation associated with this entity comes in, the selection moves too. This is an easy way to show the movement of an entity on a map or scene.

⊕ Show code …

// select a single dynamic entity
public async Task Identify(Point position, MapPoint? _)
{
    _dynamicEntityLayer.ClearSelection();

    var result = await MapViewController.IdentifyLayerAsync(_dynamicEntityLayer, position, 2d);
    if (result?.GeoElements?.FirstOrDefault() is DynamicEntityObservation observation)
    {
        SelectedEntity = observation.GetDynamicEntity();
        if (SelectedEntity is not null)
            _dynamicEntityLayer.SelectDynamicEntity(SelectedEntity);
    }
}

 

Now we can look at different ways to update the MapView or SceneView to keep the selected entity in view as it moves.

ArcGIS Maps SDK Toolkit

Check it out!

ArcGIS Maps SDKs for Native Apps Toolkit

The sample app uses the ArcGIS Maps SDK for .NET Toolkit to provide some functionality. This open-source project offers controls and components that help when developing Native Maps SDKs applications. In this application, the identify code is wrapped in an out-of-the-box feature of the GeoViewController MVVM assistant class of the toolkit. This GeoViewController is bound to our GeoView, allowing the view model code to call methods on the view without breaking Model-View-View Model priciples

The toolkit provides many other useful components for developing Native Maps SDKs applications. Consider using it in your next application.

Option 1: Manually Pan the GeoView

The first and most obvious option for following a moving DynamicEntity is to manually update the viewpoint of the MapView or SceneView to keep the entity centered in the view whenever its location changes. There are methods on the GeoView (the base class for MapView and SceneView) that make this straightforward and consistent for both 2D and 3D views.

You can be notified of changes to a DynamicEntity by listening to its DynamicEntityChanged event. This event is raised anytime a new observation is received for the DynamicEntity. When this event is triggered, you can manually update the GeoView’s viewpoint to center on the entity’s new location.

In the sample, we use the GeoView.SetViewpointAsync method to update the viewpoint whenever the selected DynamicEntity changes location. This method allows us to keep the truck centered in the view by performing an animated pan. Alternatively, you can use the SetViewpoint method to disable the pan animation. There are also many constructors for creating Viewpoints that offer various options for setting center, scale, rotation, and 3D camera values. Creative use of these methods can give you fine-grained control of the GeoView’s viewpoint in your application. Note that most of these methods are available for both the MapView and SceneView. For instance, you can pan the view to keep the truck in a corner of the map, showing more of the map in the direction it’s heading. Or you can display a more zoomed-out view based on the truck’s current speed.

The manual approach has two key benefits: it’s straightforward to implement and gives you full control over the viewpoint. However, this approach also has a drawback: it requires application code to manually update the viewpoint whenever the entity changes. Additionally, automatic updates to the viewpoint at random intervals can disrupt user interactions like panning and zooming, so it’s crucial to consider how this might affect the overall user experience and application flow.

Here is an example of how to manually follow a moving DynamicEntity using the DynamicEntityChanged event:

⊕ Show code …

private void FollowSelectedEntity_Manual()
{
  SelectedEntity.DynamicEntityChanged += SelectedEntity_DynamicEntityChanged;
}

private void StopFollowingSelectedEntity_Manual()
{
  SelectedEntity.DynamicEntityChanged -= SelectedEntity_DynamicEntityChanged;
}

private async void SelectedEntity_DynamicEntityChanged(object? sender, DynamicEntityChangedEventArgs e)
{
  if (e.ReceivedObservation?.Geometry is MapPoint point)
  {
    GeoView.SetViewpointAsync(new Viewpoint(point));
  }
}

Option 2: Custom LocationDataSource with MapView.LocationDisplay

Here is a second approach to following an entity in a 2D map: use the built-in MapView.LocationDisplay property to automatically pan the map when the entity location changes. To do this, you can create a custom LocationDataSource that uses the selected DynamicEntity as the source for location updates and then connect the new data source to the MapView.LocationDisplay property.

Custom LocationDataSource

Our CustomLocationDataSource class below inherits from the LocationDataSource class provided by the ArcGIS Maps SDK for .NET. It’s a standalone data source that can provide location updates to the LocationDisplay component of the MapView. CustomLocationDataSource listens for changes to the selected DynamicEntity using the DynamicEntityChanged event (as mentioned in option #1 above) and emits a location update accordingly.

⊕ Show code …

// Example code for creating a custom LocationDataSource
public class CustomLocationDataSource : LocationDataSource
{
  private DynamicEntity _entity;

  public CustomLocationDataSource(DynamicEntity entity)
  {
    _entity = entity;
  }

  protected override Task OnStartAsync()
  {
    _entity.DynamicEntityChanged += Entity_DynamicEntityChanged;
    return Task.CompletedTask;
  }

  protected override Task OnStopAsync()
  {
    _entity.DynamicEntityChanged -= Entity_DynamicEntityChanged;
    return Task.CompletedTask;
  }

  private void Entity_DynamicEntityChanged(object? sender, DynamicEntityChangedEventArgs e)
  {
    // Update the location based on the entity's location
    UpdateLocation(new Location((MapPoint)_entity.Geometry,
      horizontalAccuracy: 0.0, velocity: 1.0, course: 0.0, isLastKnown: false));
  }
}

 

LocationDisplay

The LocationDisplay is a built-in component of the Native Maps SDKs, mainly used to show the current device’s location on the map; however, by setting LocationDisplay.DataSource to an instance of the CustomLocationDataSource created above, we can use the LocationDisplay to show the location of our selected DynamicEntity instead. In the code below, we set the AutoPanMode property of the LocationDisplay to Recenter. This ensures the map automatically pans to keep the entity in view as it moves. The LocationDisplay has many other settings that can be used to customize the display of the entity’s location. For example, our app uses WanderEffect to tell the MapView to wait until the entity moves a certain amount before triggering a pan of the map. For more information, see the documentation for the LocationDisplay class.

⊕ Show code …

private async void FollowSelectedEntity_LocationDataSource()
{
  if (SelectedEntity is not null)
  {
    var followSource = new EntityLocationDataSource(SelectedEntity);
    await FollowEntityController.UpdateLocationDisplayAsync(followSource);
  }
}

private async void StopFollowingSelectedEntity_LocationDataSource()
{
  await FollowEntityController.UpdateLocationDisplayAsync(null);
}

public async Task UpdateLocationDisplayAsync(LocationDataSource? dataSource)
{
  if (mapView.LocationDisplay.DataSource is not null)
    await mapView.LocationDisplay.DataSource.StopAsync();

  if (dataSource is null)
  {
    mapView.LocationDisplay.DataSource = null;
    mapView.LocationDisplay.IsEnabled = false;
    return;
  }

  mapView.LocationDisplay.DataSource = dataSource;
  mapView.LocationDisplay.AutoPanMode = LocationDisplayAutoPanMode.Recenter;
  mapView.LocationDisplay.InitialZoomScale = mapView.MapScale;
  mapView.LocationDisplay.ShowLocation = false;
  mapView.LocationDisplay.WanderExtentFactor = 0.5d;
  mapView.LocationDisplay.IsEnabled = true;
}

Using the built-in LocationDisplay is a robust way to track a moving entity as the Native Maps SDK handles panning the map automatically. The underlying API turns off the automatic recenter functionality when the user interacts with the map allowing the user to pan and zoom freely. Additionally, the LocationDisplay comes with built-in symbology for displaying the location on the map, which could be useful in other scenarios, even though our sample application doesn’t utilize it.

Although creating a custom LocationDataSource requires more code, it’s worth noting that it can serve other purposes as well. For instance, it can be used as the input data source for a LocationGeotriggerFeed, which enables an application to receive notifications for Geotrigger-related events. Imagine setting up a Geotrigger to notify users when a delivery truck enters specific areas. In this scenario, the LocationGeotriggerFeed would use our custom LocationDataSource to track the truck’s location and trigger a notification when it enters one of the specified areas. This powerful feature allows you to create location-based notifications in your application.

ArcGIS Maps SDK Toolkit

Check it out!

Geotriggers

The Geotriggers API is another part of the real-time segment of the ArcGIS Maps SDKs for Native Apps. Geotriggers provide a simple way to monitor a location and alert you when it enters or leaves a geographic area. Usually, the geotrigger is configured to monitor a device location, but in this article, we hint at how a geotrigger can be set up to monitor anything that gives location updates. This opens up some interesting functionality for apps that deal with real-time entities.

See the documentation for a full discussion of the Geotrigger API: Work with Geotriggers: Kotlin.NET | Qt | Swift.

Option 3: SceneView and CameraController

If you’re working with 3D scenes, you can use a CameraController on the SceneView to track a moving DynamicEntity. To do this, set the built-in SceneView.CameraController property to an OrbitGeoElementCameraController created with the selected DynamicEntity. This forces the SceneView to follow the entity as it moves around the scene.

The OrbitGeoElementCameraController is a type of CameraController that constrains the SceneView camera to an orbit around a specified GeoElement (in this case, the selected DynamicEntity). You can customize the distance and angle of the orbit by setting the CameraController.Distance and CameraController.CameraPitchOffset properties. You can play around with these settings and others to get the desired view of the entity. Note that the CameraController handles all the DynamicEntity change events internally, so you don’t need to handle or propagate these events in your user code, which makes this approach very easy to implement.

This approach is similar to using the LocationDisplay in 2D views, but it’s designed specifically for 3D scenes. The advantage of using a CameraController is that it’s easy to implement and allows users to view the selected entity from different angles and distances interactively. However, the downside is that it only works in 3D. If you need to support both 2D and 3D views, you’ll need to implement a separate tracking mechanism for 2D maps.

Here is an example of how to use a SceneView and a CameraController to follow a moving DynamicEntity:

⊕ Show code …

private void FollowSelectedEntity_CameraController()
{
  sceneView.CameraController = new OrbitGeoElementCameraController(selectedEntity, 1000.0);
}

private void StopFollowingSelectedEntity_CameraController()
{
  sceneView.CameraController = null;
}

 

Follow DynamicEntity Summary

Option Pros Cons When to Use
#1: Manual – Simple to implement
– Ultimate control over the viewpoint
– Works for both 2D and 3D views
– Requires application code to maintain the viewpoint – In simple situations where other methods aren’t necessary
– When you need full control over the viewpoint
#2: Custom LocationDataSource – No need to manually update the viewpoint
– Can use built-in LocationDisplay settings
– Can use the custom LocationDataSource for other things (i.e. LocationGeotriggerFeed)
MapView only
– Requires extra user code for custom data source
– When you want an automated way to track a moving entity and customize the display of the entity’s location on the map
#3: CameraController – Simple to implement
– No need to manually update the viewpoint
– Can be used to get a full 3D view of the entity
SceneView only – When you need to track a moving entity in a 3D

 

Conclusion

This article explored various ways to track a moving DynamicEntity in the ArcGIS Maps SDKs for Native Apps. By using these methods, you can monitor moving objects in real-time, boosting the capabilities of your applications in both 2D and 3D.

 

Try it yourself!

I hope this article has inspired you to learn more about dynamic entities! If you’d like to learn more, we have comprehensive documentation for each of the Native Maps SDKs in our developer guides:

  • Work with dynamic entities (Native Maps SDKs Guide topic): Kotlin.NET | Qt | Swift
  • Add dynamic entity layer (Native Maps SDKs samples): Kotlin.NET | Qt | Swift
  • Custom dynamic entity data source (Native Maps SDKs samples): Kotlin.NET | Qt | Swift

You can check out the full code for the sample application described in this article on my GitHub repo. We’d love to know what apps you’re building with real-time capabilities and to hear your feedback. Suggestions are welcome over on our Esri Community Forums.

Did you know we presented a real-time technical session at the 2024 Dev Summit? You can watch it below to learn more about the key points shared in this article. Enjoy!

Share this article