Temporal statistics
MainForm.cs
// Copyright 2012 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.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Runtime.InteropServices;

using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.ADF;
using ESRI.ArcGIS.SystemUI;
using ESRI.ArcGIS.TrackingAnalyst;
using ESRI.ArcGIS.Geodatabase;
using TemporalStatistics2008;
using ESRI.ArcGIS.GeoDatabaseExtensions;

namespace TemporalStatistics
{
  public sealed partial class MainForm : Form
  {
    #region class private members
    private IMapControl3 m_mapControl = null;
    private string m_mapDocumentName = string.Empty;
    #endregion
    IWorkspaceFactory m_amsWorkspaceFactory = null;
    bool m_bTAInitialized = false;
    private const string TEMPORALLAYERCLSID = "{78C7430C-17CF-11D5-B7CF-00010265ADC5}"; //CLSID for ITemporalLayer

    #region class constructor
    public MainForm()
    {
      InitializeComponent();
    }
    #endregion

    private void MainForm_Load(object sender, EventArgs e)
    {
      //get the MapControl
      m_mapControl = (IMapControl3)axMapControl1.Object;

      //disable the Save menu (since there is no document yet)
      menuSaveDoc.Enabled = false;

      timerStats.Start();
    }

    #region Main Menu event handlers
    private void menuNewDoc_Click(object sender, EventArgs e)
    {
      //execute New Document command
      ICommand command = new CreateNewDocument();
      command.OnCreate(m_mapControl.Object);
      command.OnClick();
    }

    private void menuOpenDoc_Click(object sender, EventArgs e)
    {
      //execute Open Document command
      ICommand command = new ControlsOpenDocCommandClass();
      command.OnCreate(m_mapControl.Object);
      command.OnClick();
    }

    private void menuSaveDoc_Click(object sender, EventArgs e)
    {
      //execute Save Document command
      if (m_mapControl.CheckMxFile(m_mapDocumentName))
      {
        //create a new instance of a MapDocument
        IMapDocument mapDoc = new MapDocumentClass();
        mapDoc.Open(m_mapDocumentName, string.Empty);

        //Make sure that the MapDocument is not readonly
        if (mapDoc.get_IsReadOnly(m_mapDocumentName))
        {
          MessageBox.Show("Map document is read only!");
          mapDoc.Close();
          return;
        }

        //Replace its contents with the current map
        mapDoc.ReplaceContents((IMxdContents)m_mapControl.Map);

        //save the MapDocument in order to persist it
        mapDoc.Save(mapDoc.UsesRelativePaths, false);

        //close the MapDocument
        mapDoc.Close();
      }
    }

    private void menuSaveAs_Click(object sender, EventArgs e)
    {
      //execute SaveAs Document command
      ICommand command = new ControlsSaveAsDocCommandClass();
      command.OnCreate(m_mapControl.Object);
      command.OnClick();
    }

    private void menuExitApp_Click(object sender, EventArgs e)
    {
      //exit the application
      Application.Exit();
    }
    #endregion

        //listen to MapReplaced event in order to update the status bar and the Save menu
    private void axMapControl1_OnMapReplaced(object sender, IMapControlEvents2_OnMapReplacedEvent e)
    {
      //get the current document name from the MapControl
      m_mapDocumentName = m_mapControl.DocumentFilename;

            //if there is no MapDocument, disable the Save menu and clear the status bar
      if (m_mapDocumentName == string.Empty)
      {
        menuSaveDoc.Enabled = false;
        statusBarXY.Text = string.Empty;
      }
      else
      {
        //enable the Save menu and write the doc name to the status bar
        menuSaveDoc.Enabled = true;
        statusBarXY.Text = Path.GetFileName(m_mapDocumentName);
      }

      //Update combo list of tracking services
      PopulateTrackingServices();
    }

    private void axMapControl1_OnMouseMove(object sender, IMapControlEvents2_OnMouseMoveEvent e)
    {
      statusBarXY.Text = string.Format("{0}, {1}  {2}", e.mapX.ToString("#######.##"), e.mapY.ToString("#######.##"), axMapControl1.MapUnits.ToString().Substring(4));
    }

    //Initialize the Tracking Analyst Environment
    private ITrackingEnvironment3 setupTrackingEnv(ref object mapObj)
    {
      IExtensionManager extentionManager = new ExtensionManagerClass();

      UID uid = new UIDClass();
      uid.Value = "esriTrackingAnalyst.TrackingEngineUtil";

      ((IExtensionManagerAdmin)extentionManager).AddExtension(uid, ref mapObj);

      ITrackingEnvironment3 trackingEnv = new TrackingEnvironmentClass();
      trackingEnv.Initialize(ref mapObj);
      trackingEnv.EnableTemporalDisplayManagement = true;
      return trackingEnv;
    }

    //Periodically update the statistics information
    private void timerStats_Tick(object sender, EventArgs e)
    {
      //Initialize TA if there is hasn't been already and there are Tracking layers in the map
      if (!m_bTAInitialized && 
        GetAllTrackingLayers() != null)
      {
        object oMapControl = m_mapControl;
                ITrackingEnvironment3 taEnv = setupTrackingEnv(ref oMapControl);
                //ITrackingEnvironment3 taEnv = setupTrackingEnv(ref oMapControl);

        if (taEnv != null)
        {
          m_bTAInitialized = true;
        }

        //Need to refresh the map once to get the tracks moving
        m_mapControl.Refresh(esriViewDrawPhase.esriViewGeography, null, null);
      }

      RefreshStatistics();
    }

    private void RefreshStatistics()
    {
      try
      {
        ITemporalLayer temporalLayer = GetSelectedTemporalLayer();

        //If a temporal layer is selected in the combo box only update that layer's stats
        if (temporalLayer == null)
        {
          RefreshAllStatistics();
        }
        else
        {
          RefreshLayerStatistics(temporalLayer.Name,
            (ITemporalFeatureClassStatistics)((IFeatureLayer)temporalLayer).FeatureClass);
        }
      }
      catch (Exception ex)
      {
        statusBarXY.Text = ex.Message;
      }
    }

    //Refresh the statistics for all tracking layers in the map
    //The AMSWorkspaceFactory provides easy access to query the statistics for every layer at once
    private void RefreshAllStatistics()
    {
      try
      {
        object oNames, oValues;
        string[] sNames;

        if (m_amsWorkspaceFactory == null)
        {
          m_amsWorkspaceFactory = new AMSWorkspaceFactoryClass();
        }

        //Get the AMS Workspace Factory Statistics interface
        ITemporalWorkspaceStatistics temporalWsfStatistics = (ITemporalWorkspaceStatistics)m_amsWorkspaceFactory;
        //Get the message rates for all the tracking connections in the map
        IPropertySet psMessageRates = temporalWsfStatistics.AllMessageRates;
        psMessageRates.GetAllProperties(out oNames, out oValues);
        sNames = (string[])oNames;
        object[] oaMessageRates = (object[])oValues;

        //Get the feature counts for all the tracking connections in the map
        IPropertySet psTotalFeatureCounts = temporalWsfStatistics.AllTotalFeatureCounts;
        psTotalFeatureCounts.GetAllProperties(out oNames, out oValues);
        object[] oaFeatureCounts = (object[])oValues;

        //Get the connection status for all the tracking connections in the map
        IPropertySet psConnectionStatus = temporalWsfStatistics.ConnectionStatus;
        psConnectionStatus.GetAllProperties(out oNames, out oValues);
        string[] sConnectionNames = (string[])oNames;
        object[] oaConnectionStatus = (object[])oValues;
        Hashtable htConnectionStatus = new Hashtable(sConnectionNames.Length);
        for (int i = 0; i < sConnectionNames.Length; ++i)
        {
          htConnectionStatus.Add(sConnectionNames[i], oaConnectionStatus[i]);
        }

        //Get the track counts for all the tracking connections in the map
        IPropertySet psTrackCounts = temporalWsfStatistics.AllTrackCounts;
        psTrackCounts.GetAllProperties(out oNames, out oValues);
        object[] oaTrackCounts = (object[])oValues;

        //Get the sample sizes for all the tracking connections in the map
        IPropertySet psSampleSizes = temporalWsfStatistics.AllSampleSizes;
        psSampleSizes.GetAllProperties(out oNames, out oValues);
        object[] oaSampleSizes = (object[])oValues;

        //Clear the existing list view items and repopulate from the collection
        lvStatistics.BeginUpdate();
        lvStatistics.Items.Clear();

        //Create list view items from statistics' IPropertySets
        for (int i = 0; i < sNames.Length; ++i)
        {
          ListViewItem lvItem = new ListViewItem(sNames[i]);
          lvItem.SubItems.Add(Convert.ToString(oaMessageRates[i]));
          lvItem.SubItems.Add(Convert.ToString(oaFeatureCounts[i]));

          string sConnName = sNames[i].Split(new Char[] { '/' })[0];
          esriWorkspaceConnectionStatus eWCS = (esriWorkspaceConnectionStatus)Convert.ToInt32(htConnectionStatus[sConnName]);
          lvItem.SubItems.Add(eWCS.ToString());

          lvItem.SubItems.Add(Convert.ToString(oaTrackCounts[i]));
          lvItem.SubItems.Add(Convert.ToString(oaSampleSizes[i]));
          lvStatistics.Items.Add(lvItem);
        }

        lvStatistics.EndUpdate();
      }
      catch (System.Exception ex)
      {
        statusBarXY.Text = ex.Message;
      }
    }

    //Refresh the statistics for a single layer using the ITemporalFeatureClassStatistics
    private void RefreshLayerStatistics(string sLayerName, ITemporalFeatureClassStatistics temporalFCStats)
    {
      ListViewItem lvItem = new ListViewItem(sLayerName);
      lvItem.SubItems.Add(Convert.ToString(temporalFCStats.MessageRate));
      lvItem.SubItems.Add(Convert.ToString(temporalFCStats.TotalFeatureCount));
      lvItem.SubItems.Add("Not Available");
      lvItem.SubItems.Add(Convert.ToString(temporalFCStats.TrackCount));
      lvItem.SubItems.Add(Convert.ToString(temporalFCStats.SampleSize));

      lvStatistics.Items.Clear();
      lvStatistics.Items.Add(lvItem);
    }

    //Cause a manual refresh of the statistics, also update the timer interval
    private void btnRefresh_Click(object sender, EventArgs e)
    {
      try
      {
        timerStats.Stop();
        SetSampleSize();
        RefreshStatistics();
        double dTimerRate = Convert.ToDouble(txtRate.Text);
        timerStats.Interval = Convert.ToInt32(dTimerRate * 1000);
        timerStats.Start();
      }
      catch (Exception ex)
      {
        statusBarXY.Text = ex.Message;
      }
    }

    //Populate the combo box with the tracking services in the map
    private void PopulateTrackingServices()
    {
      ILayer lyr = null;
      IEnumLayer temporalLayers = GetAllTrackingLayers();

      cbTrackingServices.Items.Clear();
      cbTrackingServices.Items.Add("All");

      if (temporalLayers != null)
      {
        while ((lyr = temporalLayers.Next()) != null)
        {
          cbTrackingServices.Items.Add(lyr.Name);
        }
      }

      cbTrackingServices.SelectedIndex = 0;
    }

    //Query the map for all the tracking layers in it
    private IEnumLayer GetAllTrackingLayers()
    {
      try
      {
        IUID uidTemoralLayer = new UIDClass();
        uidTemoralLayer.Value = TEMPORALLAYERCLSID;

        //This call throws an E_FAIL exception if the map has no layers, caught below
        return m_mapControl.ActiveView.FocusMap.get_Layers((UID)uidTemoralLayer, true);
      }
      catch
      {
        return null;
      }
    }

    //Get the tracking layer that is selected in the combo box according to its name
    private ITemporalLayer GetSelectedTemporalLayer()
    {
      ITemporalLayer temporalLayer = null;

      if (cbTrackingServices.SelectedIndex > 0)
      {
        ILayer lyr = null;
        IEnumLayer temporalLayers = GetAllTrackingLayers();
        string selectedLayerName = cbTrackingServices.Text;

        while ((lyr = temporalLayers.Next()) != null)
        {
          if (lyr.Name == selectedLayerName)
          {
            temporalLayer = (ITemporalLayer)lyr;
          }
        }
      }

      return temporalLayer;
    }

    //Reset the statistic's feature count for one or all of the layers in the map
    private void btnResetFC_Click(object sender, EventArgs e)
    {
      try
      {
        ITemporalLayer temporalLayer = GetSelectedTemporalLayer();

        if (temporalLayer == null)
        {
          if (m_amsWorkspaceFactory == null)
          {
            m_amsWorkspaceFactory = new AMSWorkspaceFactoryClass();
          }

          //Get the AMS Workspace Factory Statistics interface
          ITemporalWorkspaceStatistics temporalWsfStatistics = (ITemporalWorkspaceStatistics)m_amsWorkspaceFactory;
          temporalWsfStatistics.ResetAllFeatureCounts();
        }
        else
        {
          ITemporalFeatureClassStatistics temporalFCStats =
            (ITemporalFeatureClassStatistics)((IFeatureLayer)temporalLayer).FeatureClass;
          temporalFCStats.ResetFeatureCount();
        }
      }
      catch (Exception ex)
      {
        statusBarXY.Text = ex.Message;
      }
    }

    //Reset the statistic's message rate for one or all of the layers in the map
    private void btnResetMsgRate_Click(object sender, EventArgs e)
    {
      try
      {
        ITemporalLayer temporalLayer = GetSelectedTemporalLayer();

        if (temporalLayer == null)
        {
          if (m_amsWorkspaceFactory == null)
          {
            m_amsWorkspaceFactory = new AMSWorkspaceFactoryClass();
          }

          //Get the AMS Workspace Factory Statistics interface
          ITemporalWorkspaceStatistics temporalWsfStatistics = (ITemporalWorkspaceStatistics)m_amsWorkspaceFactory;
          temporalWsfStatistics.ResetAllMessageRates();
        }
        else
        {
          ITemporalFeatureClassStatistics temporalFCStats =
            (ITemporalFeatureClassStatistics)((IFeatureLayer)temporalLayer).FeatureClass;
          temporalFCStats.ResetMessageRate();
        }
      }
      catch (Exception ex)
      {
        statusBarXY.Text = ex.Message;
      }
    }

    //Set the sampling size for one or all of the layers in the map
    //The sampling size determines how many messages are factored into the message rate calculation.  
    //For instance a sampling size of 500 will store the times the last 500 messages were received.
    //The message rate is calculated as the (oldest timestamp - current time) / number of messages
    private void SetSampleSize()
    {
      try
      {
        int samplingSize = Convert.ToInt32(txtSampleSize.Text);
        ITemporalLayer temporalLayer = GetSelectedTemporalLayer();

        if (temporalLayer == null)
        {
          if (m_amsWorkspaceFactory == null)
          {
            m_amsWorkspaceFactory = new AMSWorkspaceFactoryClass();
          }

          //Get the AMS Workspace Factory Statistics interface
          ITemporalWorkspaceStatistics temporalWsfStatistics = (ITemporalWorkspaceStatistics)m_amsWorkspaceFactory;
          temporalWsfStatistics.SetAllSampleSizes(samplingSize);
        }
        else
        {
          ITemporalFeatureClassStatistics temporalFCStats =
            (ITemporalFeatureClassStatistics)((IFeatureLayer)temporalLayer).FeatureClass;
          temporalFCStats.SampleSize = samplingSize;
        }
      }
      catch (Exception ex)
      {
        statusBarXY.Text = ex.Message;
      }
    }

    private void cbTrackingServices_SelectionChangeCommitted(object sender, EventArgs e)
    {
      RefreshStatistics();
    }
  }
}