Add dynamic entity layer

View inMAUIWinUIWPFView on GitHubSample viewer app

Display data from an ArcGIS stream service using a dynamic entity layer.

Image of display dynamic entity layer

Use case

A stream service is a type of service provided by ArcGIS Velocity and GeoEvent Server that allows clients to receive a stream of data observations via a web socket. ArcGIS Maps SDK for .NET allows you to connect to a stream service and manage the information as dynamic entities and display them in a dynamic entity layer. Displaying information from feeds such as a stream service is important in applications like dashboards where users need to visualize and track updates of real-world objects in real-time.

Use ArcGISStreamService to manage the connection to the stream service and purge options to manage how much data is stored and maintained by the application. The dynamic entity layer will display the latest received observation, and you can set track display properties to determine how to display historical information for each dynamic entity. This includes the number of previous observations to show, whether to display track lines in-between previous observations, and setting renderers.

How to use the sample

Use the controls to connect to or disconnect from the stream service, modify display properties in the dynamic entity layer, and purge all observations from the application.

How it works

  1. Create an ArcGIStreamService using a Uri.
  2. Set a DynamicEntityFilter on the stream service to limit the amount of data coming from the server.
  3. Set the MaximumDuration property of the stream service PurgeOptions to limit the amount of data managed by the application.
  4. Create a DynamicEntityLayer using the stream service.
  5. Update values in the layer's TrackDisplayProperties to customize the layer's appearance.
  6. Add the DynamicEntityLayer to the map.

Relevant API

  • ArcGISStreamService
  • ConnectionStatus
  • DynamicEntity
  • DynamicEntityFilter
  • DynamicEntityLayer
  • DynamicEntityPurgeOptions
  • TrackDisplayProperties

About the data

This sample uses a stream service that simulates live data coming from snowplows near Sandy, Utah. There are multiple vehicle types and multiple agencies operating the snowplows.

Additional information

More information about dynamic entities can be found in the guide documentation.

Tags

data, dynamic, entity, live, purge, real-time, service, stream, track

Sample Code

AddDynamicEntityLayer.xaml.csAddDynamicEntityLayer.xaml.csAddDynamicEntityLayer.xaml
Use dark colors for code blocksCopy
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Copyright 2023 Esri.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
// language governing permissions and limitations under the License.

using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.RealTime;
using Esri.ArcGISRuntime.Symbology;
using Esri.ArcGISRuntime.UI;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace ArcGIS.WPF.Samples.AddDynamicEntityLayer
{
    [ArcGIS.Samples.Shared.Attributes.Sample(
        name: "Add dynamic entity layer",
        category: "Layers",
        description: "Display data from an ArcGIS stream service using a dynamic entity layer.",
        instructions: "Use the controls to connect to or disconnect from the stream service, modify display properties in the dynamic entity layer, and purge all observations from the application.",
        tags: new[] { "data", "dynamic", "entity", "live", "purge", "real-time", "service", "stream", "track" })]
    public partial class AddDynamicEntityLayer
    {
        // This envelope is a limited region around Sandy, Utah. It will be the extent used by the `DynamicEntityFilter`.
        private Envelope _utahSandyEnvelope = new Envelope(new MapPoint(-112.110052, 40.718083, SpatialReferences.Wgs84), new MapPoint(-111.814782, 40.535247, SpatialReferences.Wgs84));

        private ArcGISStreamService _dynamicEntityDataSource;
        private DynamicEntityLayer _dynamicEntityLayer;

        public AddDynamicEntityLayer()
        {
            InitializeComponent();
            Initialize();
        }

        private void Initialize()
        {
            MyMapView.Map = new Map(BasemapStyle.ArcGISDarkGrayBase);

            // Create a border line and display it on the map. This line symbolizes the filter extent.
            var borderOverlay = new GraphicsOverlay();
            borderOverlay.Graphics.Add(new Graphic(_utahSandyEnvelope, new SimpleLineSymbol(SimpleLineSymbolStyle.Dash, System.Drawing.Color.Red, 2)));
            MyMapView.GraphicsOverlays.Add(borderOverlay);
            MyMapView.SetViewpoint(new Viewpoint(_utahSandyEnvelope));

            // Create the stream service. A stream service is one type of dynamic entity data source.
            var streamServiceUrl = "https://realtimegis2016.esri.com:6443/arcgis/rest/services/SandyVehicles/StreamServer";
            _dynamicEntityDataSource = new ArcGISStreamService(new Uri(streamServiceUrl));

            // Add a filter for the data source, to limit the amount of data received by the application.
            _dynamicEntityDataSource.Filter = new ArcGISStreamServiceFilter()
            {
                // Filter with an envelope.
                Geometry = _utahSandyEnvelope,

                // Use a where clause to filter by attribute values.
                WhereClause = "speed > 0"
            };

            // Set a duration for how long observation data is stored in the data source. Dynamic entity observations older than five minutes will be discarded.
            _dynamicEntityDataSource.PurgeOptions.MaximumDuration = TimeSpan.FromMinutes(5);

            // Handle notifications about connection status.
            _dynamicEntityDataSource.ConnectionStatusChanged += ServiceConnectionStatusChanged;

            // Create a layer to display the data from the stream service.
            _dynamicEntityLayer = new DynamicEntityLayer(_dynamicEntityDataSource);

            _dynamicEntityLayer.TrackDisplayProperties.ShowPreviousObservations = true;
            _dynamicEntityLayer.TrackDisplayProperties.ShowTrackLine = true;

            // Create renderers for the observations and their track lines.
            // Create a unique value renderer for the latest observations.
            var entityValues = new List<UniqueValue>();

            // The agency attribute has agencies represented by the values "3" and "4".
            entityValues.Add(new UniqueValue(string.Empty, string.Empty, new SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, System.Drawing.Color.Pink, 8), 3));
            entityValues.Add(new UniqueValue(string.Empty, string.Empty, new SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, System.Drawing.Color.Lime, 8), 4));
            var entityRenderer = new UniqueValueRenderer(new List<string> { "agency" }, entityValues, string.Empty, new SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, System.Drawing.Color.Blue, 8));
            _dynamicEntityLayer.Renderer = entityRenderer;

            // Create a unique value renderer for the track observations.
            var trackValues = new List<UniqueValue>();
            trackValues.Add(new UniqueValue(string.Empty, string.Empty, new SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, System.Drawing.Color.Pink, 3), 3));
            trackValues.Add(new UniqueValue(string.Empty, string.Empty, new SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, System.Drawing.Color.Lime, 3), 4));
            var trackRenderer = new UniqueValueRenderer(new List<string> { "agency" }, trackValues, string.Empty, new SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, System.Drawing.Color.Blue, 3));
            _dynamicEntityLayer.TrackDisplayProperties.PreviousObservationRenderer = trackRenderer;

            // Create a simple line renderer for the track lines.
            var lineRenderer = new SimpleRenderer(new SimpleLineSymbol(SimpleLineSymbolStyle.Solid, System.Drawing.Color.LightGray, 2));
            _dynamicEntityLayer.TrackDisplayProperties.TrackLineRenderer = lineRenderer;

            // Add the dynamic entity layer to the map.
            MyMapView.Map.OperationalLayers.Add(_dynamicEntityLayer);
        }

        private void ServiceConnectionStatusChanged(object sender, ConnectionStatus status)
        {
            Dispatcher.Invoke(() =>
            {
                ConnectionStatusLabel.Content = $"Status: {status}";
                ConnectionButton.IsEnabled = false;

                if (status == ConnectionStatus.Connected)
                {
                    ConnectionButton.IsEnabled = true;
                    ConnectionButton.Content = "Disconnect";
                }
                else if (status == ConnectionStatus.Disconnected)
                {
                    ConnectionButton.IsEnabled = true;
                    ConnectionButton.Content = "Connect";
                }
            });
        }

        private void LineVisibilityCheckbox_Checked(object sender, RoutedEventArgs e)
        {
            if (_dynamicEntityLayer != null && sender is CheckBox checkBox)
            {
                _dynamicEntityLayer.TrackDisplayProperties.ShowTrackLine = checkBox.IsChecked == true;
            }
        }

        private void EntityVisibilityCheckbox_Checked(object sender, RoutedEventArgs e)
        {
            if (_dynamicEntityLayer != null && sender is CheckBox checkBox)
            {
                _dynamicEntityLayer.TrackDisplayProperties.ShowPreviousObservations = checkBox.IsChecked == true;
            }
        }

        private void PurgeButton_Click(object sender, RoutedEventArgs e)
        {
            _dynamicEntityDataSource?.PurgeAllAsync();
        }

        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (_dynamicEntityLayer != null)
            {
                _dynamicEntityLayer.TrackDisplayProperties.MaximumObservations = (int)e.NewValue;
            }
        }

        private void ConnectionButton_Click(object sender, RoutedEventArgs e)
        {
            if (_dynamicEntityDataSource.ConnectionStatus == ConnectionStatus.Connected)
            {
                _dynamicEntityDataSource.DisconnectAsync();
            }
            else if (_dynamicEntityDataSource.ConnectionStatus == ConnectionStatus.Disconnected)
            {
                _dynamicEntityDataSource.ConnectAsync();
            }
        }
    }
}

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