Click or drag to resize
Code Example - SmoothGraphicAnimation

Animates a graphic smoothly between two user defined locations by calling the MapPoint.MoveTo method at regular intervals as defined by a DispatcherTimer. The distance the point is moved each time is calculated by a quintic easing function.

Code Example
Smooth Graphic Animation

This section contains selected code files from a Visual Studio project that emphasize specific ArcGIS Runtime SDK (Windows Desktop) features. For example: some code examples may accomplish the bulk of the work as a configuration property in the .xaml file and hence only the .xaml file will be shown in detail below. In other code examples, the .xaml is used to define the configuration of graphical elements for the application but the application logic is performed in the code behind, hence you may see both the .xaml and .cs/.vb files shown in detail below.

XAML
<Window x:Class="SmoothGraphicAnimation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:esri="http://schemas.esri.com/arcgis/runtime/2013"
        Height="600" Width="800">

    <Grid>

        <!-- Add a MapView Control. -->
        <esri:MapView x:Name="MapView1" Width="800" Margin="0,50,-8,9">

            <!-- Add a Map. The full extent of the world will be shown.-->
            <esri:Map x:Name="MyMap" >
                <esri:ArcGISTiledMapServiceLayer 
                    ServiceUri="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer" />
            </esri:Map>
        </esri:MapView>

        <!-- TextBlock to hold the instructions on how to use the sample code. -->
        <TextBlock Height="88" HorizontalAlignment="Left" Name="TextBlock1" VerticalAlignment="Top"  
                   TextWrapping="Wrap" Margin="0,0,0,0" />

    </Grid>

</Window>

SPECIAL NOTE: The XAML displayed above comes from a C# project. If you are a VB.NET developer, you will need to modify the text for the x:Class namespace from "SmoothGraphicAnimation.MainWindow" to be just "MainWindow".

// Animates a graphic smoothly between two user defined locations by calling the MapPoint.MoveTo method at regular intervals as defined 
// by a DispatcherTimer. The distance the point is moved each time is calculated by a quintic easing function.

namespace SmoothGraphicAnimation
{
    public partial class MainWindow : System.Windows.Window
    {

        // Graphics layer global variable to hold the starting and ending user clicked points on the map. 
        private Esri.ArcGISRuntime.Layers.GraphicsLayer _userInteractionLayer;

        // Graphics layer global variable to hold the graphic that animates (moves) between the starting and ending user points. 
        private Esri.ArcGISRuntime.Layers.GraphicsLayer _animatingLayer;

        // Global variable to hold the starting time of the animation. 
        private System.DateTime animateStartTime;

        // Global variable to determine hold long the animation lasts. 
        private int animationDuration = 5000; //Five seconds

        // Global variable for the timer used to animate (move) the graphic in the _animatingLayer. 
        private System.Windows.Threading.DispatcherTimer animationTimer;


        public MainWindow()
        {
            InitializeComponent();

            // Add instructions on how to use the sample. 
            TextBlock1.Text = "When the application loads an ArcGISTiledMapServiceLayer will display. Click on the map " + 
                "to insert a green circle starting point graphic. Then click on the map again to insert a green circle " + 
                "ending point graphic. A red circle will animate between the starting and ending points.";


            // Define the symbol, renderer, and graphics layer that will serve as the starting and ending points for the animation to 
            // occur between. The user click on the map to set the starting and ending points with will be drawn a green circles.
            Esri.ArcGISRuntime.Symbology.SimpleMarkerSymbol mySimpleMarkerSymbol = new Esri.ArcGISRuntime.Symbology.SimpleMarkerSymbol();
            mySimpleMarkerSymbol.Color = System.Windows.Media.Colors.Green;
            mySimpleMarkerSymbol.Size = 12;
            mySimpleMarkerSymbol.Style = Esri.ArcGISRuntime.Symbology.SimpleMarkerStyle.Circle;

            Esri.ArcGISRuntime.Symbology.SimpleRenderer mySimpleRenderer = new Esri.ArcGISRuntime.Symbology.SimpleRenderer();
            mySimpleRenderer.Symbol = mySimpleMarkerSymbol;

            Esri.ArcGISRuntime.Layers.GraphicsLayer myGraphicsLayer = new Esri.ArcGISRuntime.Layers.GraphicsLayer();
            myGraphicsLayer.Renderer = mySimpleRenderer;

            // Set the new created graphics layer with it's renderer and symbology to the global variable (_userInteractionLayer).
            _userInteractionLayer = myGraphicsLayer;



            // Define the symbol, renderer, and graphics layer that will serve as the animation between the two user specified points. 
            // The animated graphic will be a red circle.
            Esri.ArcGISRuntime.Symbology.SimpleMarkerSymbol mySimpleMarkerSymbol_Animated = new Esri.ArcGISRuntime.Symbology.SimpleMarkerSymbol();
            mySimpleMarkerSymbol_Animated.Color = System.Windows.Media.Colors.Red;
            mySimpleMarkerSymbol_Animated.Size = 12;
            mySimpleMarkerSymbol_Animated.Style = Esri.ArcGISRuntime.Symbology.SimpleMarkerStyle.Circle;

            Esri.ArcGISRuntime.Symbology.SimpleRenderer mySimpleRenderer_Animated = new Esri.ArcGISRuntime.Symbology.SimpleRenderer();
            mySimpleRenderer_Animated.Symbol = mySimpleMarkerSymbol_Animated;

            Esri.ArcGISRuntime.Layers.GraphicsLayer myGraphicsLayer_Animated = new Esri.ArcGISRuntime.Layers.GraphicsLayer();
            myGraphicsLayer_Animated.Renderer = mySimpleRenderer_Animated;

            // Set the new created graphics layer with it's renderer and symbology to the global variable (_animatingLayer).
            _animatingLayer = myGraphicsLayer_Animated;


            // Create a dynamic runtime function that adds the graphics layers to the map and waits for user interaction to click twice on the 
            // map to define the starting and ending points for the animation to occur between.
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = null;
            propertyChanged = (s, e) =>
            {
                if (e.PropertyName == "SpatialReference")
                {
                    MapView1.PropertyChanged -= propertyChanged;
                    AddLayers();
                    WaitforMapClick();
                }
            };
            MapView1.PropertyChanged += propertyChanged;

        }

        private void AddLayers()
        {
            // Add the two graphics layers to the map control.
            MapView1.Map.Layers.Add(_userInteractionLayer);
            MapView1.Map.Layers.Add(_animatingLayer);

        }

        private async System.Threading.Tasks.Task WaitforMapClick()
        {

            // A recursive function that occurs takes user interaction with the map to draw the starting and ending points on 
            // the map. When the count of graphic in the _userInteractionLayer equals 2 then call the functions to animate the
            // red circle graphic between the starting and ending points. 

            // Get the map point from the user click on the map. 
            Esri.ArcGISRuntime.Geometry.MapPoint m = await MapView1.Editor.RequestPointAsync();

            // Add the graphic to the _userInteractionLayer.
            Esri.ArcGISRuntime.Layers.GraphicCollection myGraphicCollection = null;
            myGraphicCollection = _userInteractionLayer.Graphics;
            Esri.ArcGISRuntime.Layers.Graphic myUserInputGraphic = new Esri.ArcGISRuntime.Layers.Graphic(m);
            myGraphicCollection.Add(myUserInputGraphic);

            // Test if the starting and ending user interaction graphics have been created.
            if (_userInteractionLayer.Graphics.Count == 2)
            {

                // Asynchronously call the function move (i.e. animate) the graphic on the map.
                await AnimateGraphic();

                // Asynchronously call the function to set how long the animation between the two user generated points should last. 
                await System.Threading.Tasks.Task.Delay(animationDuration);

                // Clear out the two global graphics layers to reset the functionality for another run.
                _userInteractionLayer.Graphics.Clear();
                _animatingLayer.Graphics.Clear();

            }

            // Call this function recursively to reset the functionality of interacting with the map again. 
            WaitforMapClick();

        }

        private async System.Threading.Tasks.Task AnimateGraphic()
        {
            // Algorithm to move the graphic point in the _animatingLayer between the user specified starting and ending points in the 
            // _userInteractionLayer. The animation of the graphic uses an Quintic Easing InOut algorithm. You can read more about 
            // quintics at the following web sites:
            // http://msdn.microsoft.com/en-us/library/system.windows.media.animation.quinticease(v=vs.110).aspx
            // http://en.wikipedia.org/wiki/Quintic_function

            // Get the graphics collection from the _UserInteractionLayer
            Esri.ArcGISRuntime.Layers.GraphicCollection myGraphicCollection = _userInteractionLayer.Graphics;

            // Get the starting point from _userInterationLayer
            Esri.ArcGISRuntime.Layers.Graphic startingGraphic = myGraphicCollection[0];
            Esri.ArcGISRuntime.Geometry.Geometry startingGeometry = startingGraphic.Geometry;
            Esri.ArcGISRuntime.Geometry.MapPoint startPoint = startingGeometry as Esri.ArcGISRuntime.Geometry.MapPoint;

            // Get the ending point from the _userInteractionLayer
            Esri.ArcGISRuntime.Layers.Graphic endingGraphic = myGraphicCollection[1];
            Esri.ArcGISRuntime.Geometry.Geometry endingGeometry = endingGraphic.Geometry;
            Esri.ArcGISRuntime.Geometry.MapPoint endingPoint = endingGeometry as Esri.ArcGISRuntime.Geometry.MapPoint;

            // Set the initial stating point graphic in the _animatingLayer to the same location as the starting location where 
            // the user clicked on the map.
            Esri.ArcGISRuntime.Geometry.MapPoint animatingPoint = new Esri.ArcGISRuntime.Geometry.MapPoint(startPoint.X, startPoint.Y);
            Esri.ArcGISRuntime.Layers.Graphic animationGraphic = new Esri.ArcGISRuntime.Layers.Graphic();
            animationGraphic.Geometry = animatingPoint;
            _animatingLayer.Graphics.Add(animationGraphic);

            // Create a new instance of the timer used to animate the graphic in the map.
            animationTimer = new System.Windows.Threading.DispatcherTimer();
            animationTimer.Interval = System.TimeSpan.FromMilliseconds(33);

            // Define the start of the animation to be now.
            animateStartTime = System.DateTime.Now;

            // Dynamic function to calculate how fast to move the animating point and it's location. 
            animationTimer.Tick += (s, ex) =>
            {
                double fraction = (System.DateTime.Now.Subtract(animateStartTime)).TotalMilliseconds / animationDuration;
                fraction = QuinticEasingInOut(fraction, 0, 1, 1);
                var x = (endingPoint.X - startPoint.X) * fraction + startPoint.X;
                var y = (endingPoint.Y - startPoint.Y) * fraction + startPoint.Y;
                animationGraphic.Geometry = animatingPoint.MoveTo(x, y);
            };

            // Begin the animation process.
            animationTimer.Start();
        }

        public double QuinticEasingInOut(double t, double b, double c, double d)
        {
            // Sub-routine function to determine the fraction of time in the quintic easing algorithm lasts before the graphic
            // point is moved. The quintic easing in/out function has the property of the animation being slower in the beginning 
            // and end of the time interval and faster in the middle.

            t /= d / 2;
            if (t < 1)
            {
                return c / 2 * t * t * t * t * t + b;
            }
            t -= 2;
            return c / 2 * (t * t * t * t * t + 2) + b;
        }

    }
}