GISer

导航

ArcEngine中实现捕捉功能



捕捉功能主要使用ArcEngine中的两个接口


1. IHitTest用于作点击测试
2. IFeatureCache 用于建立做缓存
由于数据库中有多个FeatureClass ,而每个FeatureClass又可以做多种点击测试
所以这里有会有好几种捕捉方案。
我们称呼每一个可以执行捕捉的对象叫捕捉代理,所有的代理在一个捕捉环境中
方案1:每个代理负责测试一种FeatureClass的一种点击方式
方案2:每个代理负责测试一种FeatureClass的所有点击方式
方案3:一代理负责测试所有的FeatureClass的一种点击方式
方案4:一个代理负责测试所有FeatureClass的所有点击方式
在实际使用过程中 我们使用的是第一种方案。但是我个人认为第二种方案比较好。当然这只是个人推测
没有测试数据证明。
下面给出第一种方案的代码:
/// <summary>
 /// IFeatureSnapAgent 的摘要说明。
 /// </summary>
 public interface IFeatureSnapAgent:ISnapAgent,ISnapAgentFeedback
 {
  IFeatureCache FeatureCache
  {
   get;
  }
  IFeatureClass  FeatureClass
  {
   get;
   set;
  }
  esriGeometryHitPartType HitPartType
  {
   get;
   set;
  }
  /// <summary>
  /// 为捕捉连接事件,当捕捉发生的时候,就会触发事件。
  /// </summary>
  /// <param name="handler"></param>
  void AddSnapedEventHandler(GeometrySnapedEventHandler handler);
  /// <summary>
  /// 不再监听捕捉事件
  /// </summary>
  /// <param name="handler"></param>
  void RemoveSnapedEventHandler(GeometrySnapedEventHandler handler);
 }
 /// <summary>
 /// 默认的要素捕捉代理
 /// </summary>
 public class DefaultFeatureSnapAgent :IFeatureSnapAgent,IEditEvents,ESRI.ArcGIS .esriSystem .IPersistVariant  
 {
  #region 构造函数
  /// <summary>
  /// 为代理指定别名。注意该代理目前还没有关联到任何目标FeatureClass
  /// 要使得该代理起作用,必须要为他设置FeatureClass.
  /// </summary>
  /// <param name="name">名称(请确保唯一)</param>
  public DefaultFeatureSnapAgent(string name):this(name,null)
  {
   
  }
  /// <summary>
  /// 将使用该FeatureClass的别名做代理的名称
  /// </summary>
  /// <param name="feaClass"></param>
  public DefaultFeatureSnapAgent(IFeatureClass feaClass):this(feaClass.AliasName,feaClass)
  {


  }
  /// <summary>
  /// 完全初始化捕捉代理
  /// </summary>
  /// <param name="name">名称(请确保唯一)</param>
  /// <param name="feaClass">目标FeatureClass</param>
  public DefaultFeatureSnapAgent(string name,IFeatureClass feaClass)
  {
   m_snapAgentName=name;
   m_bCacheHasCreated=false;
   m_hitPartType=esriGeometryHitPartType.esriGeometryPartNone;
   this.m_isSnapWorking=true;
   this.m_featureClass=feaClass;
   this.m_snapFeedbackText="";
   
  }
  #endregion
  #region IFeatureSnapAgent 成员
  private event GeometrySnapedEventHandler    m_snapSubsciber;
  /// <summary>
  /// FeatureClass缓冲区。
  /// </summary>
  private IFeatureCache m_featureCache;
  /// <summary>
  /// 该代理将捕捉在该FeatureClass上的Feature.和Geometry
  /// </summary>
  private IFeatureClass m_featureClass;
  /// <summary>
  /// 点击测试的有效类型。
  /// </summary>
  protected esriGeometryHitPartType m_hitPartType;
  /// <summary>
  /// 缓冲区对象是否已经被创建了。跟是否建立了缓冲没有关系。
  /// </summary>
  private bool   m_bCacheHasCreated;
  
  /// <summary>
  /// 缓冲区对象
  /// </summary>
  public IFeatureCache FeatureCache
  {
   get
   {
    return m_featureCache;
   }
  }
  /// <summary>
  /// 目标FeatureClass。SnapAgent将针对该FeatureClass做捕捉
  /// </summary>
  public IFeatureClass FeatureClass
  {
   get
   {
    return m_featureClass;
   }
   set
   {
    m_featureClass=value;
   }
  }
  
  /// <summary>
  /// 点击测试类型。哪些点击类型会被测试
  /// </summary>
  public ESRI.ArcGIS.Geometry.esriGeometryHitPartType HitPartType
  {
   get
   {
    // TODO:  添加 DefaultFeatureSnapAgent.HitPartType getter 实现
    return m_hitPartType;
   }
   set
   {
    
    m_hitPartType=value;
   }
  }


  /// <summary>
  ///  创建缓冲区对象。
  /// </summary>
  private void CreateFeatureCache()
  {
   m_featureCache=new ESRI.ArcGIS.Carto.FeatureCacheClass();
   m_bCacheHasCreated=true;
  }
  /// <summary>
  ///  填充缓冲区。如果还没有创建缓冲区对象,就先创建缓冲区对象。
  ///  如果已经拥有缓冲区,而且当前点依然在该缓冲区内部,那么不会填充生成新的缓冲。
  ///  由于缓冲是在捕捉方法内部被使用的。所以可以保证m_featureClass必然不会为空引用。
  /// </summary>
  /// <param name="point">当前点</param>
  /// <param name="size">缓冲区大小</param>
  private void FillCache(IPoint point,double size)
  {
   if(!m_bCacheHasCreated)
   {
    CreateFeatureCache();
   }
   if(!m_featureCache.Contains (point))
   {
    m_featureCache.Initialize(point,size);
    m_featureCache.AddFeatures(this.m_featureClass);
   }
  }
  /// <summary>
  /// 添加事件侦听者。捕捉发生后,事件将会被发送到该侦听者。
  /// </summary>
  /// <param name="handler"></param>
  public void AddSnapedEventHandler(GeometrySnapedEventHandler handler)
  {
   m_snapSubsciber+=handler;
  }
  /// <summary>
  /// 移去事件侦听者。
  /// </summary>
  /// <param name="handler"></param>
  public void RemoveSnapedEventHandler(GeometrySnapedEventHandler handler)
  {


   m_snapSubsciber-=handler;
  }
  #endregion
  #region ISnapAgent 成员
  private string m_snapAgentName;
  /// <summary>
  /// SnapAgent是否在工作。代表用户是打开还是关闭了SnapAgent
  /// 初始化的时候默认是打开的。
  /// </summary>
  private bool   m_isSnapWorking;
  public string Name
  {
   get
   {
    return m_snapAgentName;
   }
  }
  public bool IsWorking()
  {
   return this.m_isSnapWorking ;
  }
  /// <summary>
  ///  捕捉。
  /// </summary>
  /// <param name="metry"></param>
  /// <param name="snapPoint"></param>
  /// <param name="tolerance"></param>
  /// <returns></returns>
  public virtual bool Snap(IGeometry metry, IPoint snapPoint, double tolerance)
  {
   /*
    * 捕捉的过程:
    * 首先使用当前位置、目标图层、和误差的10倍构造一个缓冲区。
    * 对缓冲区中的每个Feature,找到他包含的每一个Geometry。
    * 对Geometry做点击测试。
    */
   if(!this.m_isSnapWorking)
   {
    //捕捉代理已经被用户关闭了。不会有任何捕捉动作发生
    return false;
   }
   if(m_featureClass==null)
   {
    //没有目标图层。不能做捕捉动作。此时应该报错
    //但是目前只是返回false。
    return false;
   }
   FillCache(snapPoint,tolerance*10);
   //当前被测试的Feature
   IFeature            feature=null;
   //当前被测试的几何图形
   IGeometry           curMetry=null;
   //当前被测试的Feature的索引和缓冲区中拥有的feature的个数。
   int featureIndex,featureCount;
   featureCount=m_featureCache.Count;
   for(featureIndex=0;featureIndex<featureCount;featureIndex++)
   {
    feature=m_featureCache.get_Feature(featureIndex);
    if(feature!=null)
    {
     curMetry=feature.Shape;
     IPoint hitPoint=new ESRI.ArcGIS .Geometry.PointClass ();
     double hitDist=0;
     int hitPartIndex=-1;
     bool bRightSide=false;
     int hitSegmentIndex=-1;
     IHitTest hitTest=(IHitTest)curMetry;
     if(hitTest.HitTest (snapPoint,tolerance,this.m_hitPartType,hitPoint,ref hitDist
      ,ref hitPartIndex,ref hitSegmentIndex,ref bRightSide))
     {


      GeometrySnapEventArgs args=new GeometrySnapEventArgs (hitPoint,curMetry,
       feature,this.m_featureClass,hitPartIndex,hitSegmentIndex,tolerance,
       hitDist,this.m_hitPartType,bRightSide);
      SetFeedback("FeatureSnapAgent"+this.Name+"捕捉到了!");
      LaunchSnapEvent(args);  
      snapPoint.X=hitPoint.X;
      snapPoint.Y=hitPoint.Y;
      return true;
     }
     
     
     
     
    }
   }


   
   return false;
  }
  
  /// <summary>
  /// 打开捕捉代理
  /// </summary>
  public void TurnOn()
  {
   this.m_isSnapWorking=true;
  }
  /// <summary>
  /// 关闭捕捉代理
  /// </summary>
  public void TurnOff()
  {
   this.m_isSnapWorking =false;
  }
  private void LaunchSnapEvent(SnapEventArgs args)
  {
   
   if(this.m_snapSubsciber!=null&&args!=null)
   {
    this.m_snapSubsciber(this,args);
   }
  }
  #endregion
  #region Object 成员
  /// <summary>
  /// 名字是一个agent的唯一标志。
  /// </summary>
  /// <param name="obj"></param>
  /// <returns></returns>
  public override bool Equals(object obj)
  {
   if(! (obj is DefaultFeatureSnapAgent))
   {
    return false;
   }
   DefaultFeatureSnapAgent agent=(DefaultFeatureSnapAgent)obj;
   return this.m_snapAgentName.Equals(agent.m_snapAgentName);
   
  }
  
  public override int GetHashCode()
  {
   return this.m_snapAgentName.GetHashCode();
  }
  #endregion
  #region IEditEvents 成员


  public void AfterDrawSketch(IObject obj)
  {
   // TODO:  添加 DefaultFeatureSnapAgent.AfterDrawSketch 实现
  }


  public void OnChangeFeature(IObject obj)
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnChangeFeature 实现
  }


  public void OnConflictsDetected()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnConflictsDetected 实现
  }


  public void OnCreateFeature(IObject obj)
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnCreateFeature 实现
  }


  public void OnCurrentLayerChanged()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnCurrentLayerChanged 实现
  }


  public void OnCurrentTaskChanged()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnCurrentTaskChanged 实现
  }


  public void OnDeleteFeature(IObject obj)
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnDeleteFeature 实现
  }


  public void OnRedo()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnRedo 实现
  }


  public void OnSelectionChanged()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnSelectionChanged 实现
  }


  public void OnSketchFinished()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnSketchFinished 实现
  }


  public void OnSketchModified()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnSketchModified 实现
  }


  public void OnStartEditing()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnStartEditing 实现
  }


  public void OnStopEditing(Boolean save)
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnStopEditing 实现
  }


  public void OnUndo()
  {
   // TODO:  添加 DefaultFeatureSnapAgent.OnUndo 实现
  }


  #endregion
  #region ISnapFeedback 成员
  private string m_snapFeedbackText;
  public string SnapText
  {
   get
   {
    return this.m_snapFeedbackText;
   }
  }
  private void SetFeedback(string feedback)
  {
   this.m_snapFeedbackText=feedback;
  }
  #endregion
  #region IPersistVariant 成员
  public ESRI.ArcGIS .esriSystem .UID  ID
  {
   get
   {
    ESRI.ArcGIS .esriSystem .UID uid=new ESRI.ArcGIS .esriSystem .UIDClass ();
    uid.Value ="ls.gis.Editor.DefaultFeatureSnapAgent"+this.m_snapAgentName;
    return uid;
   }
  }
  public void Load(ESRI.ArcGIS .esriSystem .IVariantStream vs)
  {
   this.m_snapAgentName =(string)vs.Read ();
   this.m_isSnapWorking =(bool)vs.Read ();
   string hitPartStr=(string)vs.Read ();
   this.m_hitPartType =(esriGeometryHitPartType)Enum.Parse (this.m_hitPartType .GetType (),hitPartStr,false);
   bool hasFeatureClass=(bool)vs.Read ();
   if(hasFeatureClass)
   {
    ESRI.ArcGIS .esriSystem .IName name=(ESRI.ArcGIS .esriSystem .IName)vs.Read ();
    this.m_featureClass =(IFeatureClass)name.Open ();
   }
  }
  public void Save(ESRI.ArcGIS .esriSystem .IVariantStream vs)
  {
   vs.Write (this.m_snapAgentName);
   vs.Write (this.m_isSnapWorking );
   vs.Write (this.m_hitPartType.ToString ());
   if(this.m_featureClass !=null)
   {
    vs.Write (true);
    IDataset dataset=(IDataset)this.m_featureClass ;
    vs.Write (dataset.FullName );
   }
   else
   {
    vs.Write (false);
   }
   


  }
  #endregion
  
 }



 public class DefaultSnapAgentEnvironment:ISnapAgentEnvironment
 {
  private double     m_tolerance;
  private SnapToleranceUnit   m_snapToleranceUnit;
  private ArrayList  m_snapAgentArray;
  /// <summary>
  /// 用于转换误差单位
  /// </summary>
  private IActiveView m_activeView;
  /// <summary>
  /// 如果误差单位为地图单位,那么可以调用这个构造函数。
  /// 如果误差单位为象素。那么应该调用有参数的构造方法。
  /// 如果在调用时不能确定参数activeView,那么也可以先调用该方法构造对象。
  /// 然后用属性ActiveView来设置该参数的值。
  /// </summary>
  public DefaultSnapAgentEnvironment():this(null)
  {
   
  }
  public DefaultSnapAgentEnvironment(IActiveView activeView)
  {
   m_snapAgentArray=new ArrayList ();
   m_tolerance=7;
   m_snapToleranceUnit=SnapToleranceUnit.UnitPixels;
   this.m_activeView=activeView;
  }
  /// <summary>
  ///  用于转换误差的单位。如果没有设置,或者设置为null,
  ///  那么误差的单位将不会被转换,而直接被认为是地图单位。
  /// </summary>
  public IActiveView ActivView
  {
   set
   {
    this.m_activeView=value;
   }
   get
   {
    return this.m_activeView;
   }
  }
  #region ISnapAgentEnvironment 成员  


  public void AddSnapAgent(ISnapAgent agent)
  {
   if(agent==null)
   {
    return;
   }
   if(this.m_snapAgentArray.Contains(agent))
   {
    return;
   }
   this.m_snapAgentArray.Add(agent);
  }


  public void ClearSnapAgent()
  {
   this.m_snapAgentArray.Clear();
  }
  /// <summary>
  /// 如果索引越界,那么返回null,而不会抛出异常。
  /// </summary>
  /// <param name="index"></param>
  /// <returns></returns>
  public ISnapAgent GetSnapAgent(int index)
  {
   if(index<this.m_snapAgentArray.Count&&index>=0)
   {
    return (ISnapAgent)this.m_snapAgentArray[index];
   }
   else
   {
    return null;
   }
  }
  /// <summary>
  /// 如果不存在,回返回null
  /// </summary>
  /// <param name="name"></param>
  /// <returns></returns>
  ISnapAgent ls.gis.Editor.ISnapAgentEnvironment.GetSnapAgent(string name)
  {
   ISnapAgent retAgent=null;
   int retAgentIndex=-1;
   for(int index=0; index<this.m_snapAgentArray.Count;index++)
   {
    retAgent=(ISnapAgent)this.m_snapAgentArray[index];
    if(retAgent.Name.Equals(name))
    {
     retAgentIndex=index;
     break;
    }
   }
   return GetSnapAgent(retAgentIndex);
  }


  public void RemoveSnapAgent(string name)
  {
   ISnapAgent retAgent=null;
   int retAgentIndex=-1;
   for(int index=0; index<this.m_snapAgentArray.Count;index++)
   {
    retAgent=(ISnapAgent)this.m_snapAgentArray[index];
    if(retAgent.Name.Equals(name))
    {
     retAgentIndex=index;
     break;
    }
   }
   this.RemoveSnapAgent(retAgentIndex);
  }
  /// <summary>
  ///
  /// </summary>
  /// <param name="index"></param>
  public void RemoveSnapAgent(int index)
  {
   if(index<0||index>=this.m_snapAgentArray.Count)
   {
    return ;
   }
   this.m_snapAgentArray.RemoveAt(index);
  }


  public bool SnapPoint(IPoint point)
  {
   for(int index=0;index<this.m_snapAgentArray.Count;index++)
   {
    ISnapAgent agent=(ISnapAgent)this.m_snapAgentArray[index];
    if(agent.Snap(null,point,ConvertTolerance(this.m_tolerance)))
    {      
     return true;
    }
   }
   return false;
  }


  public int SnapAgentCount
  {
   get
   {
    // TODO:  添加 FeatureSnapAgentEnvironment.SnapAgentCount getter 实现
    return this.m_snapAgentArray.Count;
   }
  }


  public double SnapAgentTolerance
  {
   get
   {
    // TODO:  添加 FeatureSnapAgentEnvironment.SnapAgentTolerance getter 实现
    return this.m_tolerance;
   }
   set
   {
    this.m_tolerance=value;
   }
  }
  public SnapToleranceUnit SnapToleranceUnit
  {
   get
   {
    return this.m_snapToleranceUnit;
   }
   set
   {
    this.m_snapToleranceUnit=value;
   }
  }
  
  #endregion
  private double ConvertTolerance(double tolerance)
  {
   double retValue=tolerance;
   if(this.m_activeView ==null)
   {
    //不能做转换
    retValue=tolerance;
   }
   else
   {
    if(this.m_snapToleranceUnit.Equals (SnapToleranceUnit.UnitPixels))
    {
     //需要转换
     retValue=ls.gis.Common .CommonCooperation.ConvertPixelsToMapUnits (this.m_activeView ,tolerance);
    }
    else
    { //不需要转换
     retValue=tolerance;
    }
   }
   return retValue;
  }
  private double ConvertTolerance()
  {
   return this.ConvertTolerance (this.m_tolerance);
  }
  


 }

posted on 2011-03-01 15:42  于小栋  阅读(976)  评论(0编辑  收藏  举报