TrackObject.cs
Dynamic display — tracking dynamic object
TrackObject.cs
// Copyright 2006 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using System;
using System.Xml;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Timers;
using System.Windows.Forms;
using ESRI.ArcGIS.Controls;
using System.Collections.Generic;
using Microsoft.Win32;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.DataSourcesFile;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.SystemUI;

namespace DynamicObjectTracking
{

  /// <summary>
  /// A helper method used to delegate calls to the main thread
  /// </summary>
  public sealed class InvokeHelper : Control
  {
    //delegate used to pass the invoked method to the main thread
    public delegate void MessageHandler(NavigationData navigationData);

    //class members
    private IActiveView m_activeView;
    private IPoint m_point = null;

    /// <summary>
    /// class constructor
    /// </summary>
    /// <param name="activeView"></param>
    public InvokeHelper(IActiveView activeView)
    {
      //make sure tha the control got created and that it has a valid handle
      this.CreateHandle();
      this.CreateControl();

      //get the active view
      m_activeView = activeView;
    }

    /// <summary>
    /// delegate the required method onto the main thread
    /// </summary>
    /// <param name="navigationData"></param>
    public void InvokeMethod(NavigationData navigationData)
    {
      try
      {
        // Invoke the HandleMessage through its delegate
        if (!this.IsDisposed && this.IsHandleCreated)
          Invoke(new MessageHandler(CenterMap), new object[] { navigationData });
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    /// <summary>
    /// the method that gets executed by the delegate
    /// </summary>
    /// <param name="navigationData"></param>
    public void CenterMap(NavigationData navigationData)
    {
      try
      {
        //get the current map visible extent
        IEnvelope envelope = m_activeView.ScreenDisplay.DisplayTransformation.VisibleBounds;
        if (null == m_point)
        {
          m_point = new PointClass();
        }
        //set the new map center coordinate
        m_point.PutCoords(navigationData.X, navigationData.Y);
        //center the map around the new coordinate
        envelope.CenterAt(m_point);
        m_activeView.ScreenDisplay.DisplayTransformation.VisibleBounds = envelope;
        //rotate the map to the new rotation angle
        m_activeView.ScreenDisplay.DisplayTransformation.Rotation = navigationData.Azimuth;
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    /// <summary>
    /// control's initialization
    /// </summary>
    private void InitializeComponent()
    {
    }
  }

  /// <summary>
  /// A user defined data structure used to pass information to the invoke method
  /// </summary>
  public struct NavigationData
  {
    public double X;
    public double Y;
    public double Azimuth;

    /// <summary>
    /// struct constructor
    /// </summary>
    /// <param name="x">map x coordinate</param>
    /// <param name="y">map x coordinate</param>
    /// <param name="azimuth">the new map azimuth</param>
    public NavigationData(double x, double y, double azimuth)
    {
        X = x;
        Y = y;
        Azimuth = azimuth;
    }
  }

  /// <summary>
  /// This command triggers the tracking functionality using Dynamic Display
  /// </summary>
  [Guid("803D4188-AB2F-49f9-9340-42C809887063")]
  [ComVisible(true)]
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("DynamicObjectTracking.TrackObject")]
  public sealed class TrackObject : BaseCommand
  {
    #region COM Registration Function(s)
    [ComRegisterFunction()]
    [ComVisible(false)]
    static void RegisterFunction(Type registerType)
    {
      // Required for ArcGIS Component Category Registrar support
      ArcGISCategoryRegistration(registerType);

      //
      // TODO: Add any COM registration code here
      //
    }

    [ComUnregisterFunction()]
    [ComVisible(false)]
    static void UnregisterFunction(Type registerType)
    {
      // Required for ArcGIS Component Category Registrar support
      ArcGISCategoryUnregistration(registerType);

      //
      // TODO: Add any COM unregistration code here
      //
    }

    #region ArcGIS Component Category Registrar generated code
    /// <summary>
    /// Required method for ArcGIS Component Category registration -
    /// Do not modify the contents of this method with the code editor.
    /// </summary>
    private static void ArcGISCategoryRegistration(Type registerType)
    {
      string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
      ControlsCommands.Register(regKey);
      MxCommands.Register(regKey);
    }
    /// <summary>
    /// Required method for ArcGIS Component Category unregistration -
    /// Do not modify the contents of this method with the code editor.
    /// </summary>
    private static void ArcGISCategoryUnregistration(Type registerType)
    {
      string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
      ControlsCommands.Unregister(regKey);
      MxCommands.Unregister(regKey);
    }

    #endregion
    #endregion

    //class members
    private IHookHelper           m_hookHelper      = null;
    private string                m_navigationDataFileName   = string.Empty;
    private IPoint                m_point           = null;
    private List<WKSPoint>        m_points          = null;
    private System.Timers.Timer   m_timer           = null;
    private InvokeHelper          m_invokeHelper    = null;
    private ObjectLocationClass   m_objectLocation  = null;
    private static int            m_pointIndex      = 0;
    private bool                  m_bIsRunning      = false;
    private bool                  m_bOnce           = true;

    /// <summary>
    /// class constructor
    /// </summary>
    public TrackObject()
    {
      base.m_category = ".NET Samples"; 
      base.m_caption = "Track Dynamic Object";  
      base.m_message = "Tracking a dynamic object";   
      base.m_toolTip = "Tracking a dynamic object";   
      base.m_name = "DotNetSamples.TrackDynamicObject";   

      try
      {
        string bitmapResourceName = GetType().Name + ".bmp";
        base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
      }
    }

    #region Overriden Class Methods

    /// <summary>
    /// Occurs when this command is created
    /// </summary>
    /// <param name="hook">Instance of the application</param>
    public override void OnCreate(object hook)
    {
      if (hook == null)
        return;

      if (m_hookHelper == null)
        m_hookHelper = new HookHelperClass();

      m_hookHelper.Hook = hook;

      //get the ArcGIS path from the registry
      RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\ESRI\ArcGIS");
      string path = Convert.ToString(key.GetValue("InstallDir"));

      //set the path to the featureclass used by the GPS simulator
      m_navigationDataFileName = System.IO.Path.Combine(path, @"DeveloperKit\SamplesNET\data\USAMajorHighways\NavigationData.xml");
   
      m_point = new PointClass();
      m_points = new List<WKSPoint>();

      //instantiate the timer
      m_timer = new System.Timers.Timer(60);
      m_timer.Enabled = false;
      //set the timer's elapsed event handler
      m_timer.Elapsed += new ElapsedEventHandler(OnTimerElapsed);
    }

    /// <summary>
    /// Occurs when this command is clicked
    /// </summary>
    public override void OnClick()
    {
      //create the Invoke helper class
      if (null == m_invokeHelper)
        m_invokeHelper = new InvokeHelper(m_hookHelper.ActiveView);

      //make sure to switch into dynamic mode
      IDynamicMap dynamicMap = (IDynamicMap)m_hookHelper.FocusMap;
      if (!dynamicMap.DynamicMapEnabled)
        dynamicMap.DynamicMapEnabled = true;

      //do some initializations...
      if (m_bOnce)
      {
        //generate the navigation data
        GenerateNavigationData();

        //initialize the dynamic layer which will be used to draw the tracked object
        m_objectLocation = new ObjectLocationClass();

        //add the dynamic layer to the map
        m_hookHelper.FocusMap.AddLayer(m_objectLocation);
        m_bOnce = false;
      }

      //Start the tracking timer
      if(!m_bIsRunning)
        m_timer.Enabled = true;
      else
        m_timer.Enabled = false;

      //set the running flag
      m_bIsRunning = !m_bIsRunning;
    }

    /// <summary>
    /// set the state of the button of the command
    /// </summary>
    public override bool Checked
    {
      get
      {
        return m_bIsRunning;
      }
    }

    #endregion

    /// <summary>
    /// Timer elapsed event handler
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnTimerElapsed(object sender, ElapsedEventArgs e)
    {
      try
      {
        //make sure that the current tracking point index does not exceed the list index
        if (m_pointIndex == (m_points.Count - 1))
        {
          m_timer.Enabled = false;
          return;
        }

        //get the current and the next track location
        WKSPoint currentPoint = m_points[m_pointIndex];
        WKSPoint nextPoint = m_points[m_pointIndex + 1];

        //calculate the azimuth to the next location
        double azimuth = (180.0 / Math.PI) * Math.Atan2(nextPoint.X - currentPoint.X, nextPoint.Y - currentPoint.Y);

        //create the navigation data structure
        NavigationData navigationData = new NavigationData(currentPoint.X, currentPoint.Y, azimuth);

        //update the map extent and rotation
        m_invokeHelper.InvokeMethod(navigationData);

        //add the navigation data to the loction layer in order to draw it
        m_objectLocation.AddNavigationData(navigationData);

        //increment the tracking point index
        m_pointIndex++;
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    private void GenerateNavigationData()
    {
      try
      {
        if (!System.IO.File.Exists(m_navigationDataFileName))
        {
          throw new Exception("File " + m_navigationDataFileName + " cannot be found!");
        }

        XmlTextReader reader = new XmlTextReader(m_navigationDataFileName);

        XmlDocument doc = new XmlDocument();
        doc.Load(reader);

        reader.Close();

        double X;
        double Y;
        //get the navigation items
        XmlNodeList nodes = doc.DocumentElement.SelectNodes("./navigationItem");
        foreach (XmlNode node in nodes)
        {
          X = Convert.ToDouble(node.Attributes[0].Value);
          Y = Convert.ToDouble(node.Attributes[1].Value);

          WKSPoint p = new WKSPoint();
          p.X = X;
          p.Y = Y;
          m_points.Add(p);
        }
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }
  }
}