捕捉功能主要使用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);
}
}