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);
??}
??
?}