CustomLayerBase.cs
Dynamic Display layer sample
CustomLayerBase.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.Collections;
using System.Data;
using System.Runtime.InteropServices;
using System.Windows.Forms;

using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Display;


namespace MyDynamiicLayer
{

  /// <summary>
  /// DynamicLayerBase serves as a BaseClass for DynamicLayers.
  /// </summary>
  /// <remarks>In order to implemet a DynamicLayer using this base-class, the user has to
  /// inherit this base class and override method DrawDynamicLayer().</remarks>
  public abstract class DynamicLayerBase : Control, ILayer, IDynamicLayer, IGeoDataset, IPersistVariant, ILayerGeneralProperties, ILayerExtensions, IEnumerable, IDisposable
  {
    #region Class members
    /// <summary>
    /// Keep the layer's extent. Returned by the ILayer::Extent property
    /// </summary>
    /// <remarks>The extent should be spatial-referenced to the DateFrame's spatial reference.
    /// </remarks>
    protected IEnvelope m_extent = null;

    /// <summary>
    /// Store the layer's underlying data spatial reference. Returned by IGeoDataset::SpatialReference.
    /// </summary>
    /// <remarks>This spatial reference should not be reprojected. In your inheriting 
    /// class you will need to have another parameter that will keep the DataFrame's spatial reference
    /// that would use to reproject the geometries and the extent of the layer.</remarks>
    protected ISpatialReference m_spatialRef = null;

    /// <summary>
    /// Layer's name. Returned by ILayer::Name property
    /// </summary>
    protected string m_sName;

    /// <summary>
    /// Flag which determines whether the layers is visible. Returned by ILayer::Visible
    /// </summary>
    /// <remarks>You should use this member in your inherited class in the Draw method.</remarks>
    protected bool m_visible;

    /// <summary>
    /// determines whether the layers is cached
    /// </summary>
    protected bool m_IsCached;

    /// <summary>
    /// Flag thich determine whether the layer is valid (connected to its data source, has valid information etc.).
    /// Returned by ILAyer::Valid.
    /// </summary>
    /// <remarks>You can use this flag to determine for example whether the layer can be available or not.</remarks>
    protected bool m_bValid = true;

    /// <summary>
    /// Keep the maximum scale value at which the layer will display
    /// </summary>
    protected double m_MaximumScale;

    /// <summary>
    /// Keep the minimum scale value at which the layer will display
    /// </summary>
    protected double m_MinimumScale;

    /// <summary>
    /// determines whether the layers is supposed to show its MapTips
    /// </summary>
    protected bool m_ShowTips;

    /// <summary>
    /// the layer dirty flag which determine whether its display list need to be recreate
    /// </summary>
    protected bool m_bIsImmediateDirty = false;

    /// <summary>
    /// the layer dirty flag which determine whether its display list need to be recreate
    /// </summary>
    protected bool m_bIsCompiledDirty = false;

    /// <summary>
    /// The rate in which the DynamicDisplay recompile its display lists
    /// </summary>
    protected int m_nRecompileRate = -1;

    /// <summary>
    /// The layer's UID
    /// </summary>
    protected UID m_uid;

    /// <summary>
    /// An arraylist to store the layer's extensions.
    /// </summary>
    protected ArrayList m_extensions = null;


    /// <summary>
    /// The data structure for the custom layer
    /// </summary>
    /// <remarks>By default, the DataTable gets initialized in the BaseClass Ctor with an ID column
    /// which is AutoIncremented. The BaseClass also support enumerator which is actually en enumerator of
    /// the table as well as a direct indexer.</remarks>
    protected DataTable m_table;
    #endregion

    #region class constructor

    /// <summary>
    /// Default constructor
    /// </summary>
    public DynamicLayerBase()
    {
      m_sName = string.Empty;
      m_visible = true;
      m_IsCached = false;
      m_MaximumScale = 0;
      m_MinimumScale = 0;
      m_table = new DataTable("RECORDS");

      m_table.Columns.Add("ID", typeof(long));

      m_extent = new EnvelopeClass();
      m_extent.SetEmpty();


      m_uid = new UIDClass();

      m_extensions = new ArrayList();

      //make sure tha the control got created and that it has a valid handle
      this.CreateHandle();
      this.CreateControl();
    }
    #endregion

    #region IGeoDataset Members

    /// <summary>
    ///The layers geodataset extent which is a union of the extents of all
    /// the items of the layer
    /// </summary>
    /// <remarks>In your inheriting class, consider the following code to calculate the layer's extent:
    /// <code>
    /// public override IEnvelope Extent
    ///{
    ///  get
    ///  {
    ///    m_extent  = GetLayerExtent();
    ///    if (null == m_extent )
    ///      return null;
    ///
    ///    IEnvelope env = ((IClone)m_extent ).Clone() as IEnvelope;
    ///    if(null != m_layerSpatialRef)
    ///       env.Project(m_layerSpatialRef);
    /// 
    ///    return env;
    ///  }
    ///}
    ///    private IEnvelope GetLayerExtent()
    ///{
    ///  if (null == base.m_spRef)
    ///  {
    ///    base.m_spRef = CreateGeographicSpatialReference();
    ///  }
    ///
    ///  IEnvelope env = new EnvelopeClass();
    ///  env.SpatialReference = base.m_spRef;
    ///  IPoint point = new PointClass();
    ///  point.SpatialReference = m_spRef;
    ///  foreach (DataRow r in m_table.Rows)
    ///  {
    ///    point.Y = Convert.ToDouble(r["Y"]);
    ///    point.X = Convert.ToDouble(r["X"]);
    ///
    ///    env.Union(point.Envelope);
    ///  }
    /// 
    ///  return env;
    ///} 
    /// </code>
    /// </remarks>    
    virtual public IEnvelope Extent
    {
      get
      {
        return m_extent;
      }
    }

    /// <summary>
    /// The spatial reference of the underlying data.
    /// </summary>
    /// <remarks>The property must return the underlying data spatial reference and
    /// must not reporoject it into the layer's spatial reference </remarks>

    virtual public ISpatialReference SpatialReference
    {
      get
      {
        return m_spatialRef;
      }
    }

    #endregion

    #region IPersistVariant Members

    /// <summary>
    /// The ID of the object.
    /// </summary>
    virtual public UID ID
    {
      get
      {
        // TODO:  Add clsCustomLayer.ID getter implementation
        return null;
      }
    }

    /// <summary>
    /// Loads the object properties from the stream.
    /// </summary>
    /// <param name="Stream"></param>
    /// <remarks>The Load method must read the data from the stream in the same order the data was 
    /// written to the stream in the Save method. 
    /// Streams are sequential; you mut ensure that your data is saved and loaded in the correct order, 
    /// so that the correct data is written to the correct member.
    /// </remarks>
    virtual public void Load(IVariantStream Stream)
    {
      m_extensions = (ArrayList)Stream.Read();
    }

    /// <summary>
    /// Saves the object properties to the stream.
    /// </summary>
    /// <param name="Stream"></param>
    virtual public void Save(IVariantStream Stream)
    {
      Stream.Write(m_extensions);
    }

    #endregion

    #region ILayer Members

    #region Properties

    /// <summary>
    /// Indicates if the layer shows map tips.
    /// </summary>
    /// <remarks>Indicates whether or not map tips are shown for the layer. 
    /// If set to True, then map tips will be shown for the layer. 
    /// You can determine the text that will be shown via TipText. 
    ///</remarks>
    virtual public bool ShowTips
    {
      get
      {
        return m_ShowTips;
      }
      set
      {
        m_ShowTips = value;
      }
    }

    /// <summary>
    /// The default area of interest for the layer. Returns the spatial-referenced extent of the layer.
    /// </summary>
    virtual public IEnvelope AreaOfInterest
    {
      get
      {
        return m_extent;
      }
    }

    /// <summary>
    /// Indicates if the layer is currently visible.
    /// </summary>
    virtual new public bool Visible
    {
      get
      {
        return m_visible;
      }
      set
      {
        m_visible = value;
      }
    }


    /// <summary>
    /// Indicates if the layer needs its own display cache.
    /// </summary>
    /// <remarks>This property indicates whether or not the layer requires its own display cache. 
    /// If this property is True, then the Map will use a separate display cache for the layer so 
    /// that it can be refreshed indpendently of other layers.</remarks>
    virtual public bool Cached
    {
      get
      {
        return m_IsCached;
      }
      set
      {
        m_IsCached = value;
      }
    }

    /// <summary>
    /// Minimum scale (representative fraction) at which the layer will display.
    /// </summary>
    /// <remarks>Specifies the minimum scale at which the layer will be displayed. 
    /// This means that if you zoom out beyond this scale, the layer will not display. 
    /// For example, specify 1000 to have the layer not display when zoomed out beyond 1:1000.</remarks>
    virtual public double MinimumScale
    {
      get
      {
        return m_MinimumScale;
      }
      set
      {
        m_MinimumScale = value;
      }
    }

    /// <summary>
    /// Indicates if the layer is currently valid.
    /// </summary>
    /// <remarks>The valid property indicates if the layer is currently valid.
    /// Layers that reference feature classes are valid when they hold a reference to a valid feature class.
    /// The property does not however validate the integrity of the feature classes reference to the database.
    /// Therefore, in rare situations if a datasource is removed after a layer is initialized, 
    /// the layer will report itself as valid but query attempts to the data source will error due to the lack 
    /// of underlying data.</remarks>
    virtual public bool Valid
    {
      get
      {
        return m_bValid;
      }
    }

    /// <summary>
    /// The Layer name.
    /// </summary>
    virtual new public string Name
    {
      get
      {
        return m_sName;
      }
      set
      {
        m_sName = value;
      }
    }

    /// <summary>
    /// Maximum scale (representative fraction) at which the layer will display.
    /// </summary>
    /// <remarks>Specifies the maximum scale at which the layer will be displayed. 
    /// This means that if you zoom in beyond this scale, the layer will not display. 
    /// For example, specify 500 to have the layer not display when zoomed in beyond 1:500.</remarks>
    virtual public double MaximumScale
    {
      get
      {
        return m_MaximumScale;
      }
      set
      {
        m_MaximumScale = value;
      }
    }

    /// <summary>
    /// Supported draw phases.
    /// </summary>
    /// <remarks>Indicates the draw phases supported by the layer (esriDPGeography, esriDPAnnotation, 
    /// esriDPSelection, or any combination of the three). 
    /// The supported draw phases are defined by esriDrawPhase. 
    /// When multiple draw phases are supported, the sum of the constants is used. 
    /// For example, if SupportedDrawPhases = 3 then the layer supports drawing in the geography and annotation phases.</remarks>
    public int SupportedDrawPhases
    {
      get
      {
        return (int)esriDrawPhase.esriDPGeography;
      }
    }

    /// <summary>
    /// Spatial reference for the layer.
    /// </summary>
    ///<remarks>This property is only used for map display, setting this property does not 
    ///change the spatial reference of the layer's underlying data. 
    ///The ArcGIS framework uses this property to pass the spatial reference from the map 
    ///to the layer in order to support on-the-fly projection.</remarks> 
    ISpatialReference ESRI.ArcGIS.Carto.ILayer.SpatialReference
    {
      set
      {
        m_spatialRef = value;
      }
    }

    #endregion

    #region Methods
    /// <summary>
    /// Map tip text at the specified location. 
    /// </summary>
    /// <param name="X"></param>
    /// <param name="Y"></param>
    /// <param name="Tolerance"></param>
    /// <returns>The text string that gets displayed as a map tip if ShowTips = true.</returns>
    virtual public string get_TipText(double X, double Y, double Tolerance)
    {
      return null;
    }

    /// <summary>
    /// Draws the layer to the specified display for the given draw phase.
    /// </summary>
    /// <param name="drawPhase"></param>
    /// <param name="Display"></param>
    /// <param name="trackCancel"></param>
    /// <remarks>This method draws the layer to the Display for the specified DrawPhase. 
    /// Use the TrackCancel object to allow the drawing of the layer to be interrupted by the user.
    /// In order to implement you inheriting class, you must override this method</remarks>
    public virtual void Draw(esriDrawPhase drawPhase, IDisplay Display, ITrackCancel trackCancel)
    {
      return;
    }
    #endregion
    #endregion

    #region IDynamicLayer Members

    /// <summary>
    /// The dynamic layer draw method
    /// </summary>
    /// <param name="DynamicDrawPhase">the current drawphase of the drawing</param>
    /// <param name="Display">the ActiveView's display</param>
    /// <param name="DynamicDisplay">the ActiveView's dynamic display</param>
    /// <remarks>This method is set as abstract, which means that the inherited class must override it</remarks>
    public abstract void DrawDynamicLayer(esriDynamicDrawPhase DynamicDrawPhase, IDisplay Display, IDynamicDisplay DynamicDisplay);

    /// <summary>
    /// get the dirty flag of the layer
    /// </summary>
    /// <param name="DynamicDrawPhase"></param>
    /// <returns></returns>
    public virtual bool get_DynamicLayerDirty(esriDynamicDrawPhase DynamicDrawPhase)
    {
      switch (DynamicDrawPhase)
      {
        case esriDynamicDrawPhase.esriDDPCompiled:
          return m_bIsCompiledDirty;
        case esriDynamicDrawPhase.esriDDPImmediate:
          return m_bIsImmediateDirty;
      }

      return false;
    }

    /// <summary>
    /// set the dirty flag of the layer
    /// </summary>
    /// <param name="DynamicDrawPhase"></param>
    /// <param name="Dirty"></param>
    public virtual void set_DynamicLayerDirty(esriDynamicDrawPhase DynamicDrawPhase, bool Dirty)
    {
      switch (DynamicDrawPhase)
      {
        case esriDynamicDrawPhase.esriDDPCompiled:
          m_bIsCompiledDirty = Dirty;
          break;
        case esriDynamicDrawPhase.esriDDPImmediate:
          m_bIsImmediateDirty = Dirty;
          break;
      }
    }

    /// <summary>
    /// This is the rate in which the DynamicDisplay builds the Display-Lists of the 'compiled'
    /// DrawPhase
    /// </summary>
    public virtual int DynamicRecompileRate 
    {
      get { return m_nRecompileRate; }
    }

    #endregion

    #region ILayerGeneralProperties Members

    /// <summary>
    /// Last maximum scale setting used by layer.
    /// </summary>
    virtual public double LastMaximumScale
    {
      get 
      {
        return 0;    
      }
    }

    /// <summary>
    /// Last minimum scale setting used by layer.
    /// </summary>
    virtual public double LastMinimumScale
    {
      get
      {
        return 0;
      }
    }

    /// <summary>
    /// Description for the layer.
    /// </summary>
    virtual public string LayerDescription
    {
      get 
      {
        return null;
      }
      set 
      { 

      }
    }
    #endregion

    #region ILayerExtensions Members

    /// <summary>
    /// Removes the specified extension.
    /// </summary>
    /// <param name="Index"></param>
    public virtual void RemoveExtension(int Index)
    {
      if (Index < 0 || Index > m_extensions.Count - 1)
        return;

      m_extensions.RemoveAt(Index);
    }

    /// <summary>
    /// Number of extensions.
    /// </summary>
    public virtual int ExtensionCount
    {
      get
      {
        return m_extensions.Count;
      }
    }

    /// <summary>
    /// Adds a new extension.
    /// </summary>
    /// <param name="ext"></param>
    public virtual void AddExtension(object ext)
    {
      if (null == ext)
        return;

      m_extensions.Add(ext);
    }

    /// <summary>
    /// The extension at the specified index. 
    /// </summary>
    /// <param name="Index"></param>
    /// <returns></returns>
    public virtual object get_Extension(int Index)
    {
      if (Index < 0 || Index > m_extensions.Count - 1)
        return null;

      return m_extensions[Index];
    }

    #endregion

    #region Data Structure basic functionality
    /// <summary>
    /// Create a new item (does not add it to the layer)
    /// </summary>
    /// <returns></returns>
    virtual public DataRow NewItem()
    {
      return m_table.NewRow();
    }

    /// <summary>
    /// Add an item to the layer
    /// </summary>
    /// <param name="row"></param>
    virtual public void AddItem(DataRow row)
    {
      m_table.Rows.Add(row);
    }

    /// <summary>
    /// Add an item to the Layer
    /// </summary>
    /// <param name="values"></param>
    virtual public void AddItem(object[] values)
    {
      m_table.Rows.Add(values);
    }

    /// <summary>
    /// Query for items in the layer
    /// </summary>
    /// <param name="queryFilter">WHERE clause</param>
    /// <returns></returns>
    virtual public DataRow[] Select(string queryFilter)
    {
      return m_table.Select(queryFilter);
    }

    /// <summary>
    /// Remove all items in the layer
    /// </summary>
    virtual public void Clear()
    {
      m_table.Rows.Clear();
    }

    /// <summary>
    /// Remove the item from the layer
    /// </summary>
    /// <param name="row">The row to remove</param>
    virtual public void RemoveItem(DataRow row)
    {
      m_table.Rows.Remove(row);
    }

    //indexer. Pass an index and return a record
    public DataRow this[int index]
    {
      get
      {
        return m_table.Rows[index];
      }
    }

    /// <summary>
    /// return the number of geometries in the layer
    /// </summary>
    public int NumOfRecords
    {
      get
      {
        return m_table.Rows.Count;
      }
    }
    #endregion

    #region IEnumerable Members

    /// <summary>
    /// Allow users to directly enumerate through the layer's records
    /// </summary>
    /// <returns></returns>
    public IEnumerator GetEnumerator()
    {
      return m_table.Rows.GetEnumerator();
    }

    #endregion

    #region IDisposable Members

    /// <summary>
    /// Dispose the layer
    /// </summary>
    public new void Dispose()
    {
      m_extent = null;
      m_spatialRef = null;

      base.Dispose();
    }

    #endregion

  }
}