捕捉功能主要使用ArcEngine中的两个接口
1. IHitTest用于作点击测试
2. IFeatureCache 用于建立做缓存
由于数据库中有多个FeatureClass ,而每个FeatureClass又可以做多种点击测试
所以这里有会有好几种捕捉方案。
我们称呼每一个可以执行捕捉的对象叫捕捉代理,所有的代理在一个捕捉环境中
方案1:每个代理负责测试一种FeatureClass的一种点击方式
方案2:每个代理负责测试一种FeatureClass的所有点击方式
方案3:一代理负责测试所有的FeatureClass的一种点击方式
方案4:一个代理负责测试所有FeatureClass的所有点击方式
在实际使用过程中 我们使用的是第一种方案。但是我个人认为第二种方案比较好。当然这只是个人推测
没有测试数据证明。
下面给出第一种方案的代码:
未测试过,部门接口只是用与Desktop
/// <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); } }
浙公网安备 33010602011771号