ArcObjects Library Reference  

BufferSnap

About the Buffer snap agent Sample

[C#]

BufferSnap.cs

using System;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Controls;

namespace BufferSnapCS
{
  [Guid("44BDCF61-5CD0-41f3-A934-8CAFCD931DEF")]
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("BufferSnapCS.BufferSnap")]

  /// <summary>
  /// Uses the Create Feature event to turn on the extension, which 
  /// implements a snapping agent. The Buffer Snap agent is based on a buffer
  /// around the points of the first editable point feature class.
  /// A buffer of 1000 map units is created and if the next point feature created
  /// is within the tolerance it is snapped to the buffer ring. 
  /// </summary>
  public sealed class BufferSnap : IEngineSnapAgent, IEngineSnapAgentCategory, IPersistVariant, IExtension
  {

    #region COM Registration Function(s)
    [ComRegisterFunction()]
    [ComVisible(false)]
    static void RegisterFunction(Type registerType)
    {
      // Required for ArcGIS Component Category Registrar support
      ArcGISCategoryRegistration(registerType);
    }

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

    }

    #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);
      EngineSnapAgents.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);
      EngineSnapAgents.Unregister(regKey);

    }

    #endregion
    #endregion

    //declare and initialize class variables.
    private IFeatureCache     m_featureCache;
    private IFeatureClass     m_featureClass;
    private IEngineEditor     m_engineeditor;

    public BufferSnap()
    {
    }

    #region "IPersistVariant Implementations"

    public ESRI.ArcGIS.esriSystem.UID ID
    {
      get
      {
        UID uID = new UIDClass();
        uID.Value = "BufferSnapCS.BufferSnap";
        return uID;
      }
    }

    public void Load(ESRI.ArcGIS.esriSystem.IVariantStream Stream)
    {
    }

    public void Save(ESRI.ArcGIS.esriSystem.IVariantStream Stream)
    {
    }

    #endregion

    #region "IEngineSnapAgent Implementations"

    public string Name
    {
      get
      {
        return "Buffer Snap CS";
      }     
    }

    public bool Snap(ESRI.ArcGIS.Geometry.IGeometry geom,
      ESRI.ArcGIS.Geometry.IPoint point, double tolerance)
    {
      GetFeatureClass();

      bool b_setNewFeatureCache = false;
      
      if (m_featureClass == null || m_engineeditor == null)
        return false;

      if (m_featureClass.ShapeType != esriGeometryType.esriGeometryPoint)
        return false;

      //Check if a feature cache has been created.
      if (!b_setNewFeatureCache)
      {
        m_featureCache = new FeatureCache();
        b_setNewFeatureCache = true;
      }

      //Fill the cache with the geometries. 
      //It is up to the developer to choose an appropriate value
      //given the map units and the scale at which editing will be undertaken.
      FillCache(m_featureClass, point, 10000);
      
      IProximityOperator proximityOp = point as IProximityOperator;
      double minDist = tolerance;

      IPoint cachePt = new PointClass();
      IPoint snapPt = new PointClass();
      IPolygon outPoly = new PolygonClass();
      ITopologicalOperator topoOp;

      IFeature feature;
      int Index = 0;
      for (int Count = 0; Count < m_featureCache.Count; Count++)
      {
        feature = m_featureCache.get_Feature(Count);
        cachePt = feature.Shape as IPoint;
        topoOp = cachePt as ITopologicalOperator;

        //Set the buffer distance to an appropriate value
        //given the map units and data being edited
        outPoly = topoOp.Buffer(1000) as IPolygon;

        double Dist = proximityOp.ReturnDistance(outPoly);
        if (Dist < minDist)
        {
          Index = Count;
          minDist = Dist;
        }
      }

      //Make sure minDist is within the search tolerance.
      if (minDist >= tolerance)
        return false;

      //Retrieve the feature and its part again.
      feature = m_featureCache.get_Feature(Index);
      cachePt = feature.Shape as IPoint;
      topoOp = cachePt as ITopologicalOperator;

      //Set the buffer distance to an appropriate value
      //given the map scale and data being edited
      outPoly = topoOp.Buffer(1000) as IPolygon;
      proximityOp = outPoly as IProximityOperator;
      snapPt = proximityOp.ReturnNearestPoint(point,esriSegmentExtension.esriNoExtension);

      //Since point was passed in ByValue, we have to modify its values instead.
      //of giving it a new address.
      point.PutCoords(snapPt.X, snapPt.Y);

      return true;
    
    }

    private void FillCache(ESRI.ArcGIS.Geodatabase.IFeatureClass FClass,
      ESRI.ArcGIS.Geometry.IPoint pPoint, double Distance)
    {
      m_featureCache.Initialize(pPoint, Distance);
      m_featureCache.AddFeatures(FClass);
    }

    #endregion

    private void GetFeatureClass()
    {
      IMap map = m_engineeditor.Map as IMap;
      IEngineEditLayers snapLayers = m_engineeditor as IEngineEditLayers;
      IFeatureLayer featLayer = snapLayers.TargetLayer as IFeatureLayer;

      //Search the editable layers and set the snap feature class to the point layer.
      for (int CountLayers = 0; CountLayers < map.LayerCount; CountLayers++)
      {
        if (featLayer == null)
          return;

        if (featLayer.FeatureClass.ShapeType != esriGeometryType.esriGeometryPoint)
        {
          return;
        }
        else
        {
          m_featureClass = featLayer.FeatureClass;
        }
      }
    }

    #region "IExtension Members"

    public void Shutdown()
    {
      m_engineeditor = null;
      m_featureCache = null;
    }

    public void Startup(ref object initializationData)
    {
      if (initializationData != null && initializationData is IEngineEditor)
      {
        m_engineeditor = (IEngineEditor)initializationData;
      }

    #endregion
    }


    #region "IEngineSnapAgentCategory Members"

    string IEngineSnapAgentCategory.Category
    {
      get {return ("Buffer Snap Category CS");}
    }

    #endregion
  }
}

[Visual Basic .NET]

BufferSnap.vb

Imports System
Imports System.Runtime.InteropServices
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.ADF.CATIDs
Imports ESRI.ArcGIS.Controls
 
Namespace BufferSnapVB
  
  '/ <summary>
  '/ Uses the Create Feature event to turn on the extension, which 
  '/ implements a snapping agent. The Buffer Snap agent is based on a buffer
  '/ around the points of the first editable point feature class.
  '/ A buffer of 1000 map units is created if the next point feature created
  '/ is within the tolerance it is snapped to the buffer ring. 
  '/ </summary>
  <Guid("A7BE542E-6C0D-423f-8824-FFC7B6ADF0B4"), ClassInterface(ClassInterfaceType.None), ProgId("BufferSnapVB.BufferSnap")> _
  Public Class BufferSnap
    Implements IEngineSnapAgent
    Implements IEngineSnapAgentCategory
    Implements IPersistVariant
        Implements IExtension


#Region "COM Registration Function(s)"
    <ComRegisterFunction(), ComVisible(False)> _
    Public Shared Sub RegisterFunction(ByVal registerType As Type)
      ' Required for ArcGIS Component Category Registrar support
      ArcGISCategoryRegistration(registerType)
    End Sub

    <ComUnregisterFunction(), ComVisible(False)> _
    Public Shared Sub UnregisterFunction(ByVal registerType As Type)
      ' Required for ArcGIS Component Category Registrar support
      ArcGISCategoryUnregistration(registerType)

    End Sub

#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 Shared Sub ArcGISCategoryRegistration(ByVal registerType As Type)
      Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
      EngineSnapAgents.Register(regKey)


    End Sub
    '/ <summary>
    '/ Required method for ArcGIS Component Category unregistration -
    '/ Do not modify the contents of this method with the code editor.
    '/ </summary>
    Private Shared Sub ArcGISCategoryUnregistration(ByVal registerType As Type)
      Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
      EngineSnapAgents.Unregister(regKey)

    End Sub

#End Region
#End Region

    'declare and initialize class variables.
    Private m_featureCache As IFeatureCache
    Private m_featureClass As IFeatureClass
    Private m_editor As IEngineEditor

    Public Sub New()
    End Sub

#Region "IPersist Variant Members."

    ''' <summary>
    ''' Get the ID of the object.
    ''' </summary>
    Private ReadOnly Property ID() As ESRI.ArcGIS.esriSystem.UID Implements ESRI.ArcGIS.esriSystem.IPersistVariant.ID
      Get
        Dim pID As New UID
        pID.Value = "BufferSnapVB.BufferSnap"
        Return pID
      End Get
    End Property

    Private Sub Load(ByVal stream As ESRI.ArcGIS.esriSystem.IVariantStream) Implements ESRI.ArcGIS.esriSystem.IPersistVariant.Load

    End Sub

    Private Sub Save(ByVal Stream As ESRI.ArcGIS.esriSystem.IVariantStream) Implements ESRI.ArcGIS.esriSystem.IPersistVariant.Save

    End Sub
#End Region

#Region "IEngineSnapAgent Implementations"

    Public ReadOnly Property Name() As String Implements IEngineSnapAgent.Name, IExtension.Name
      Get
        Return "Buffer Snap VB"
      End Get
    End Property


    Public Function Snap(ByVal geom As IGeometry, ByVal point As IPoint, ByVal tolerance As Double) As Boolean Implements IEngineSnapAgent.Snap
      GetFeatureClass()

      Dim b_setNewFeatureCache As Boolean = False

      If m_featureClass Is Nothing Or m_editor Is Nothing Then
        Return False
      End If

      If m_featureClass.ShapeType <> esriGeometryType.esriGeometryPoint Then
        Return False
      End If

      'Check if a feature cache has been created.
      If Not b_setNewFeatureCache Then
        m_featureCache = New FeatureCache()
        b_setNewFeatureCache = True
      End If

      'Fill the New Cache with the geometries.
      'It is up to the developer to choose an appropriate value
      'given the map units and the scale at which editing will be undertaken.
      FillCache(m_featureClass, point, 10000)
       
      Dim proximityOp As IProximityOperator = DirectCast(point, IProximityOperator)
      Dim minDist As Double = tolerance
      Dim cachePt As IPoint = New PointClass()
      Dim snapPt As IPoint = New PointClass()
      Dim outPoly As IPolygon = New PolygonClass()
      Dim topoOp As ITopologicalOperator

      Dim feature As IFeature
      Dim Index As Integer = 0
      Dim Count As Integer
      For Count = 0 To m_featureCache.Count - 1 Step Count + 1
        feature = m_featureCache.Feature(Count)
        cachePt = feature.Shape
        topoOp = cachePt

        'Set the buffer distance to an appropriate value
        'given the map units and data being edited
        outPoly = topoOp.Buffer(1000)

        Dim Dist As Double = proximityOp.ReturnDistance(outPoly)
        If Dist < minDist Then
          Index = Count
          minDist = Dist
        End If
      Next

      'Make sure minDist is within the search tolerance.
      If minDist >= tolerance Then
        Return False
      End If

      'Retrieve the feature and its part again.
      feature = m_featureCache.Feature(Index)
      cachePt = feature.Shape
      topoOp = cachePt

      'Set the buffer distance to an appropriate value
      'given the map units and data being edited
      outPoly = topoOp.Buffer(1000)
      proximityOp = outPoly
      snapPt = proximityOp.ReturnNearestPoint(point, esriSegmentExtension.esriNoExtension)

      'Since point was passed in ByValue, we have to modify its values instead.
      'of giving it a new address.
      point.PutCoords(snapPt.X, snapPt.Y)

      Return True

    End Function

    Private Sub FillCache(ByVal FClass As IFeatureClass, ByVal pPoint As IPoint, ByVal Distance As Double)
      m_featureCache.Initialize(pPoint, Distance)
      m_featureCache.AddFeatures(FClass)
    End Sub

#End Region

#Region "IEngineSnapAgentCategory Implementation"
    Public ReadOnly Property Category() As String Implements IEngineSnapAgentCategory.Category
      Get
        Return "Buffer Snap Category VB"
      End Get
    End Property
#End Region

    Private Sub GetFeatureClass()
      Dim map As IMap = m_editor.Map
      Dim snapLayers As IEngineEditLayers = m_editor
      Dim featLayer As IFeatureLayer = snapLayers.TargetLayer

      'Search the editable layers and set the snap feature class to the point layer.
      Dim CountLayers As Integer
      For CountLayers = 0 To map.LayerCount - 1 Step CountLayers + 1
        If featLayer Is Nothing Then
          Return
        End If

        If featLayer.FeatureClass.ShapeType <> esriGeometryType.esriGeometryPoint Then
          Return
        Else
          m_featureClass = featLayer.FeatureClass
        End If
      Next
    End Sub

#Region "IExtension Members"

    Public Sub Shutdown() Implements IExtension.Shutdown
      m_editor = Nothing
    End Sub

    Public Sub Startup(ByRef initializationData As Object) Implements IExtension.Startup
      If initializationData IsNot Nothing AndAlso TypeOf initializationData Is IEngineEditor Then
        m_editor = DirectCast(initializationData, IEngineEditor)
      End If


    End Sub
#End Region
  End Class
End Namespace