zoukankan      html  css  js  c++  java
  • 我的需求2

    ArcGIS Editor工具,实现要素拖动、编辑

    ArcGIS Editor工具,实现要素拖动、编辑

    本来根本没有写这个工具的意思,自己一直用的Ae自带的工 具。虽然可控制力弱了一些,但终究还是凑或能用。首先,说一下,所有的要素的更新、插入、删除最好放在编辑的Session 里面,即以一对StartEditing和StopEditing包裹。
        一开始我的疑问在于,为什么我用IWorkspaceEdit的StartEditing方法后,怎么鼠标不会变成AcrMap里面Editor的开始编 辑那样呢,为什么不能选中要素,为什么不能双击后编辑要素?原来,选中要素和使要素处于编辑状态还是要自己写的(经高人指点,呵呵)。
        先介绍一下程序中用到的变量:
    复制内容到剪贴板
    代码:
    private AxMapControl m_MapControl;//从外部传进来的MapControl控件
    private ILayer m_SelectedLayer;//当前正在编辑的图层
    private bool m_IsEdited = false;//当前是否处于编辑状态
    private bool m_IsInUse = false;//这个其实我这里没有用到,这个实在创建新要素时用到的
    private List m_SelectedFeature;//这个存储鼠标点击下,击中的要素
    private IPoint m_CurrentMousePosition;//这个好像也没用到,记录当前鼠标的位置
    private IDisplayFeedback m_FeedBack;//这个就是当前屏幕(鼠标动作)的反馈信息
    那我们就按照逻辑顺序来一一展示这个到底是怎么做的。首先是,开始和结束编辑的代码: 复制内容到剪贴板
    代码:

    ///
    /// 开始编辑
    ///
    ///
    public void StartEditing(bool bWithUndoRedo)
    {
    if (m_SelectedLayer == null) return;
    IFeatureLayer featureLayer = m_SelectedLayer as IFeatureLayer;
    if (featureLayer == null) return;
    IFeatureClass featureClass = featureLayer.FeatureClass;
    if (featureClass == null) return;

    IDataset dataset = featureClass as IDataset;
    IWorkspaceEdit workspaceEdit = dataset.Workspace as IWorkspaceEdit;
    try
    {
       workspaceEdit.StartEditing(bWithUndoRedo);
       m_IsEdited = true;
    }
    catch
    {
       return;
    }
    }
    当然你可以直接传进来IWorkspace,这个可根据你具体的需求,我这里用当前图层获取工作空间。



    ///
    /// 结束编辑
    ///
    ///
    public void StopEditing(bool bSave)
    {
    if (m_IsEdited)
    {
       m_IsEdited = false;

       if (m_SelectedLayer == null) return;
       IFeatureLayer featureLayer = m_SelectedLayer as IFeatureLayer;
       if (featureLayer == null) return;
       IFeatureClass featureClass = featureLayer.FeatureClass;
       if (featureClass == null) return;

       IDataset dataset = featureClass as IDataset;
       IWorkspaceEdit workspaceEdit = dataset.Workspace as IWorkspaceEdit;
        if (workspaceEdit.IsBeingEdited())
        {
       try
       {
              workspaceEdit.StopEditing(bSave);
           }
           catch
           {
          workspaceEdit.AbortEditOperation();
          return;
          }
       }
    }
    }

    开始编辑已经好了,然后就是鼠标点击要素时候,让其处于选中状态,我这里为了方便控制,我分成了两个函数写,给为看官也可以合并成一个:

    复制内容到剪贴板
    代码:
    public void GetFeatureOnMouseDown(int x, int y)
    {
    m_SelectedFeature.Clear();
    try
    {
           if (m_SelectedLayer == null) return;
           IFeatureLayer featureLayer = m_SelectedLayer as IFeatureLayer;
           if (featureLayer == null) return;
           IFeatureClass featureClass = featureLayer.FeatureClass;
           if (featureClass == null) return;

           IPoint point = m_MapControl.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(x, y);
           IGeometry geometry = point as IGeometry;

           double length = ConvertPixelsToMapUnits(4);
           ITopologicalOperator pTopo = geometry as ITopologicalOperator;
           IGeometry buffer = pTopo.Buffer(length);
           geometry = buffer.Envelope as IGeometry;

           ISpatialFilter spatialFilter = new SpatialFilterClass();
           spatialFilter.Geometry = geometry;
           switch (featureClass.ShapeType)
           {
             case esriGeometryType.esriGeometryPoint:
                       spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelContains;
                       break;
             case esriGeometryType.esriGeometryPolygon:
                       spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
                       break;
             case esriGeometryType.esriGeometryPolyline:
                       spatialFilter.SpatialRel = esriSpatialRelEnum.esriSpatialRelCrosses;
                       break;
           }
           spatialFilter.GeometryField = featureClass.ShapeFieldName;
           IQueryFilter filter = spatialFilter as IQueryFilter;

           IFeatureCursor cursor = featureClass.Search(filter, false);
           IFeature pfeature = cursor.NextFeature();
           while (pfeature != null)
           {
                 m_SelectedFeature.Add(pfeature);
                 pfeature = cursor.NextFeature();
           }
    }
    catch
    {
           return;
    }
    }

    ///
    /// 根据鼠标点击位置使击中要素处于高亮显示状态
    ///
    ///
    ///
    public void SelectOnMouseDown()
    {
       try
       {
              if (m_SelectedLayer == null) return;
              m_MapControl.Map.ClearSelection();
              m_MapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, null, null);
              foreach (IFeature feature in m_SelectedFeature.ToArray())
              {
                m_MapControl.Map.SelectFeature(m_SelectedLayer, feature);
              }
              m_MapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeoSelection, null, null);
       }
       catch
       { return; }
    }

    相信大家对第一个函数并不陌生吧?对的,就是用的空间查询,把点击中的要素放进容器,再用第二个函数进行高亮显示。这个地方注意,如果数据量比较多,强烈推荐用PartialRefresh 而不要用Refresh,参数的含义请参考帮助。

    这个只是开始编辑后,可以用鼠标点击选中要素,使其处于高亮状态,然而,ArcMap里面双击要素怎么出现节点用以编辑呢?这个是要自己绘制到MapControl上面去的:

    复制内容到剪贴板
    代码:
    ///
    /// 在要素上面绘制一个可拖拽的符号
    ///
    ///
    public void DrawEditSymbol(IGeometry geometry, IDisplay display)
    {
       IEngineEditProperties engineProperty = new EngineEditorClass();

       ISymbol pointSymbol = engineProperty.SketchVertexSymbol as ISymbol;
       ISymbol sketchSymbol = engineProperty.SketchSymbol as ISymbol;

       ITopologicalOperator pTopo = geometry as ITopologicalOperator;

       sketchSymbol.SetupDC(display.hDC, display.DisplayTransformation);
       sketchSymbol.Draw(pTopo.Boundary);

       IPointCollection pointCol = geometry as IPointCollection;
       for (int i = 0; i < pointCol.PointCount; i++)
       {
             IPoint point = pointCol.get_Point(i);
             pointSymbol.SetupDC(display.hDC, display.DisplayTransformation);
             pointSymbol.Draw(point);
             pointSymbol.ResetDC();
       }

       ESRI.ArcGIS.ADF.ComReleaser.ReleaseCOMObject(sketchSymbol);
       ESRI.ArcGIS.ADF.ComReleaser.ReleaseCOMObject(pointSymbol);
       ESRI.ArcGIS.ADF.ComReleaser.ReleaseCOMObject(engineProperty);
    }

    注意,这个函数是在MapControl的AfterDraw里面调用的,IDisplay参数,是AfterDraw事件的参数,我这里就没有画出来,类似ArcMap里面还有一个红色的节点,这里全是绿色的,因为红色对我的用处不大,也就没有必要了,当然各位看官可以自行修改,还有就是绘制符号的时候一定要记得释放内存哦~
        现在,编辑状态做好了,但是现在只是雏形,因为它还没有响应任何鼠标事件,当鼠标悬停在绿色的节点上时并不会让你可以拖拽,所以我们还要做点工作。
        那现在就开始如果鼠标悬停在绿色节点上时的代码吧:

    复制内容到剪贴板
    代码:
    public void SnapVertex(int x,int y,IGeometry snapContainer,ref bool vertexSnaped,ref bool contained)
    {
       IPoint point = m_MapControl.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(x, y);
       IPoint pHitPoint = null;
       double hitDist = -1, tol = -1;
       int vertexIndex = -1, partIndex = -1;
       bool vertex = false;

       tol = ConvertPixelsToMapUnits(4);

       IHitTest pHitTest = snapContainer as IHitTest;
       bool bHit=pHitTest.HitTest(point, tol, esriGeometryHitPartType.esriGeometryPartVertex, pHitPoint, ref    hitDist, ref partIndex, ref vertexIndex, ref vertex);
       vertexSnaped = false;
       contained = false;
       if (bHit)
       {
             m_MapControl.MousePointer = esriControlsMousePointer.esriPointerCrosshair;
             vertexSnaped = true;
             return;
       }
       else
       {
             IRelationalOperator pRelOperator=null;
             ITopologicalOperator pTopo = null;
             IGeometry buffer = null;
             IPolygon polygon = null;
             switch (snapContainer.GeometryType)
             {
                   case esriGeometryType.esriGeometryPolyline:
                         pTopo = snapContainer as ITopologicalOperator;
                         buffer=pTopo.Buffer(3);
                         polygon = buffer as IPolygon;
                         pRelOperator=polygon as IRelationalOperator;
                         break;
                   case esriGeometryType.esriGeometryPolygon:
                         polygon = snapContainer as IPolygon;
                         pRelOperator=polygon as IRelationalOperator;
                         break;
                   case esriGeometryType.esriGeometryPoint:
                         pTopo = snapContainer as ITopologicalOperator;
                         buffer = pTopo.Buffer(3);
                         polygon = buffer as IPolygon;
                         pRelOperator = polygon as IRelationalOperator;
                         break;
                   default:
                         break;
             }

             if (pRelOperator == null) return;
             if (pRelOperator.Contains(point))
             {
                   m_MapControl.MousePointer = esriControlsMousePointer.esriPointerSizeAll;
                   contained = true;
             }
             else m_MapControl.MousePointer = esriControlsMousePointer.esriPointerArrow;
             return;
       }
    }

    这里就是根据鼠标悬停的位置,来改变鼠标样式,抱歉的很,我没有从AE里面找到和ArcMap一 样的鼠标样式,只要用一些简单的替代了,当然你可以自己定义,也可以打电话、发Email质问ESRI客服,如果你知道了,一定要告诉我,否则我诅咒你找 不到女朋友!这个函数里面,我需要判断,鼠标是悬停在要素的节点上,还是悬停在要素内部了,这个对于后面处理是拽节点还是移动要素有很大的帮助。

    这个根据鼠标悬停位置改变鼠标样式,还远远不够滴,当你悬停到绿色节点上时,按下鼠标时,就意味这拖 拽开始,当鼠标左键按下并移动时,要出现像橡皮筋一样的连接线,当左键弹起时,就意味着拖拽结束,如果再按下左键,就意味着要素的编辑结束。这个怎么做到 呢,请看下面的代码:
    public bool EditFeature(int x,int y,IGeometry geometry)
    {
        GetFeatureOnMouseDown(x, y);
        SelectOnMouseDown();
        if(m_SelectedFeature.Count<1)return false;
        if (geometry == null) return false;

        IPoint pHitPoint=null;
        double hitDist = 0, tol = 0;
        int vertexIndex = 0, vertexOffset = 0, numVertices = 0, partIndex = 0;
        bool vertex=false;

        IFeature editedFeature = m_SelectedFeature[0];
        point = m_MapControl.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(x, y);
        tol = ConvertPixelsToMapUnits(4);
        //IGeometry pGeo = editedFeature.Shape;
        //m_EditingFeature = editedFeature;

        try
        {
           switch (geometry.GeometryType)
           {
               case esriGeometryType.esriGeometryPoint:
                            m_FeedBack = new MovePointFeedbackClass();
                            m_FeedBack.Display = m_MapControl.ActiveView.ScreenDisplay;
                            IMovePointFeedback pointMove = m_FeedBack as IMovePointFeedback;
                            pointMove.Start(geometry as IPoint, point);
                            break;
               case esriGeometryType.esriGeometryPolyline:
               if (TestGeometryHit(tol, point, geometry, ref pHitPoint, ref hitDist, ref partIndex, ref vertexOffset, ref vertexIndex, ref vertex))
               {
                    if(!vertex)
                    {
                          IGeometryCollection geometryColl = geometry as IGeometryCollection;
                          IPath path = geometryColl.get_Geometry(partIndex) as IPath;
                          IPointCollection pointColl = path as IPointCollection;
                          numVertices = pointColl.PointCount;
                          object missing = Type.Missing;

                          if (vertexIndex == 0)
                          {
                                object start = 1 as object;
                                pointColl.AddPoint(point, ref start, ref missing);
                          }
                          else
                          {
                                object objVertexIndex=vertexIndex as object;
                                pointColl.AddPoint(point, ref missing, ref objVertexIndex);
                          }
                          TestGeometryHit(tol, point, geometry, ref pHitPoint, ref hitDist, ref partIndex, ref vertexOffset, ref vertexIndex, ref vertex);
                     }
                     m_FeedBack = new LineMovePointFeedbackClass();
                     m_FeedBack.Display = m_MapControl.ActiveView.ScreenDisplay;
                     ILineMovePointFeedback lineMove = m_FeedBack as ILineMovePointFeedback;
                     lineMove.Start(geometry as IPolyline, vertexIndex, point);
               }
               else return false;
               break;
               case esriGeometryType.esriGeometryPolygon:
               if (TestGeometryHit(tol, point, geometry, ref pHitPoint, ref hitDist, ref partIndex, ref vertexOffset, ref vertexIndex, ref vertex))
               {
                    if(!vertex)
                    {
                        IGeometryCollection geometryColl = geometry as IGeometryCollection;
                        IPath path = geometryColl.get_Geometry(partIndex) as IPath;
                        IPointCollection pointColl = path as IPointCollection;
                        numVertices = pointColl.PointCount;
                        object missing = Type.Missing;
                        if (vertexIndex == 0)
                        {
                            object start = 1 as object;
                            pointColl.AddPoint(point, ref start, ref missing);
                        }
                        else
                        {
                            object objVertexIndex = vertexIndex as object;
                            pointColl.AddPoint(point, ref missing, ref objVertexIndex);
                        }
                        TestGeometryHit(tol, point, geometry, ref pHitPoint, ref hitDist, ref partIndex, ref vertexOffset, ref vertexIndex, ref vertex);
                    }
                    m_FeedBack = new PolygonMovePointFeedbackClass();
                    m_FeedBack.Display = m_MapControl.ActiveView.ScreenDisplay;
                    IPolygonMovePointFeedback polyMove = m_FeedBack as IPolygonMovePointFeedback;
                    polyMove.Start(geometry as IPolygon, vertexIndex + vertexOffset, point);
                }
                else return false;
                break;
                default:
                break;
            }
         }
         catch {return false;}
         return true;
    }

    public void FeatureEditMouseMove(int x,int y)
    {
       if (m_FeedBack == null) return;
       IPoint point = m_MapControl.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(x, y);
       m_FeedBack.MoveTo(point);
    }

    public IGeometry EndFeatureEdit(int x, int y)
    {
        if (m_FeedBack == null) return null;

        IGeometry geometry = null;
        IPoint point = m_MapControl.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(x, y);
        if ((m_FeedBack as IMovePointFeedback) != null)
        {
             IMovePointFeedback pointMove = m_FeedBack as IMovePointFeedback;
             geometry = pointMove.Stop() as IGeometry;
        }
        else if ((m_FeedBack as ILineMovePointFeedback) != null)
        {
             ILineMovePointFeedback lineMove = m_FeedBack as ILineMovePointFeedback;
             geometry = lineMove.Stop() as IGeometry;
        }
        else if ((m_FeedBack as IPolygonMovePointFeedback) != null)
        {
             IPolygonMovePointFeedback polyMove = m_FeedBack as IPolygonMovePointFeedback;
             geometry = polyMove.Stop() as IGeometry;
        }
        m_FeedBack = null;
        m_MapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, m_SelectedLayer, null);
        return geometry;
    }

    呵,这个排版真的是很累啊,无法自动缩进,害的我只能一行一行的移动。第一个函数,自然是点击下绿色 节点时触发,意味着,我们的拖拽就开始了;但是,但按着鼠标左键来回移动时怎么处理啊?呵呵,这就是第二个函数的所具备的功能,所以你要在 MapControl 的MouseMove事件中添加这个函数;但是当你确定你移动的位置是最佳位置的时候,想停下来怎么办呢?这就是第三个函数来帮你了,也就意味着你的拖拽 结束,并返回一个IGeometry,这个IGeometry就是你编辑过后要素的新的几何形状,并且当你移动结束左键弹起的时候,这个新的 IGeometry也需要显示出来它的绿色的节点,所以重绘的时候需要这个IGeometry。     好了,我们快大功告成了,stop!好像还少点东西,对了,我们最终是要把最新的几何形状赋给编辑的要素以实现更新,所以我们还需要下面的这个函数:

    复制内容到剪贴板
    代码:
    public bool UpdateEdit(IGeometry newGeom)
    {
       if (m_SelectedFeature.Count < 1) return false;
       if (newGeom == null) return false;
       if (newGeom.IsEmpty) return false;

       IFeature feature = m_SelectedFeature[0];
       IDataset dataset = feature.Class as IDataset;
       IWorkspaceEdit workspaceEdit = dataset.Workspace as IWorkspaceEdit;
       if (!workspaceEdit.IsBeingEdited()) return false;

       workspaceEdit.StartEditOperation();
       feature.Shape = newGeom;
       feature.Store();
       workspaceEdit.StopEditOperation();
       m_SelectedFeature.Clear();
       m_SelectedFeature.Add(feature);
       ClearSelection();

       return true;
    }

    呵呵,对的,这个函数很简单,就是更新一下编辑要素的几何形状,注意啊,这里我获取当前编辑的要素是从容器中取出来的,这样是不是很方便啊?这样我们就可以更新一个要素了。别忘了最后记得调用StopEditing以保存你的编辑!
    当然,不可能一次性做好,我们需要更人性化一点,我们需要添加撤销和重做:

    复制内容到剪贴板
    代码:
    public void UndoEdit()
    {
       if (m_SelectedLayer == null) return;

       IFeatureLayer featLayer = m_SelectedLayer as IFeatureLayer;
       IDataset dataset = featLayer.FeatureClass as IDataset;
       IWorkspaceEdit workspaceEdit = dataset.Workspace as IWorkspaceEdit;
       bool bHasUndos = false;
       workspaceEdit.HasUndos(ref bHasUndos);
       if(bHasUndos)
       {
             workspaceEdit.UndoEditOperation();
       }
       ClearSelection();
    }

    public void RedoEdit()
    {
       if (m_SelectedLayer == null) return;

       IFeatureLayer featLayer = m_SelectedLayer as IFeatureLayer;
       IDataset dataset = featLayer.FeatureClass as IDataset;
       IWorkspaceEdit workspaceEdit = dataset.Workspace as IWorkspaceEdit;
       bool bHasUndos = false;
       workspaceEdit.HasRedos(ref bHasUndos);
       if (bHasUndos)
       {
             workspaceEdit.RedoEditOperation();
       }
       ClearSelection();
    }


    呵呵 这个也是很简单,对吧?这里需要给出几个上述函数中用到的辅助函数,这里就不一一阐明了,请各位看官自行分析:

    代码:
    private double ConvertPixelsToMapUnits(double pixelUnits)
    {
       int pixelExtent = m_MapControl.ActiveView.ScreenDisplay.DisplayTransformation.get_DeviceFrame().right-    m_MapControl.ActiveView.ScreenDisplay.DisplayTransformation.get_DeviceFrame().left;

       double realWorldDisplayExtent =          m_MapControl.ActiveView.ScreenDisplay.DisplayTransformation.VisibleBounds.Width;
       double sizeOfOnePixel = realWorldDisplayExtent / pixelExtent;

    r    eturn pixelUnits * sizeOfOnePixel;
    }

    private bool TestGeometryHit(double tol,IPoint pPoint,IGeometry geometry,ref IPoint pHitPoint,
    ref double hitDist, ref int partIndex, ref int vertexOffset,
    ref int vertexIndex, ref bool vertexHit)
    {
       IHitTest pHitTest = geometry as IHitTest;
       pHitPoint = new PointClass();
       bool last = true;
       bool res = false;
       if (pHitTest.HitTest(pPoint, tol, esriGeometryHitPartType.esriGeometryPartVertex, pHitPoint, ref hitDist, ref partIndex, ref vertexIndex, ref last))
       {
             vertexHit = true;
             res = true;
       }
       else
       {
             if (pHitTest.HitTest(pPoint, tol, esriGeometryHitPartType.esriGeometryPartBoundary, pHitPoint, ref hitDist, ref partIndex, ref vertexIndex, ref last))
             {
                   vertexHit = false;
                   res = true;
             }
       }

       if(partIndex>0)
       {
             IGeometryCollection pGeoColl = geometry as IGeometryCollection;
             vertexOffset = 0;
             for (int i = 0; i < partIndex;i=2*i+1)
             {
                   IPointCollection pointColl = pGeoColl.get_Geometry(i) as IPointCollection;
                   vertexOffset = vertexOffset + pointColl.PointCount;
             }
       }
       return res;
    }

    ///
    /// 清除要素选择状态,恢复常态
    ///
    public void ClearSelection()
    {
       m_MapControl.Map.ClearSelection();
       m_MapControl.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, m_SelectedLayer, null);
    }
    各位看官,由于要素的移动、要素的创建和要素编辑在代码逻辑上差不多,所以我就不做过多阐述了,都是在处理鼠标的事件,需要做判断。其实,可能有看官已经了解,在AE的帮助里面就有类似的代码,是的,我参考了这部分代码,并对ESRI的开发和工作人员表示感谢。 
        希望大家能够有所启发,谢谢大家的支持!
    posted @ 2009-02-20 13:32 增强信任、促进沟通 阅读(25) 评论(0)  编辑 收藏 网摘 所属分类: .NETArcGIS Engine
    td { font-size: 12px; } .commentTextBox { font-family: Verdana; font-size: 13px; } a.blue:visited, a.blue:active, a.blue:link, a.blue:hover { color: blue; } .userData { behavior: url(#default#userdata); }

  • 相关阅读:
    windows xp查看缩略图时有缩略图没有文件名
    数据库的相关操作
    使用timer控件创建一个简单的报警程序
    xp_sendmail的正确配置与使用
    SQL Server 索引结构及其使用(三)
    启动与关闭服务器
    不间断连续图片滚动效果的制作方法
    使用C#调用外部Ping命令获取网络连接情况
    SQL Server 索引结构及其使用(一)
    winform 与asp.net 下拉列表的区别
  • 原文地址:https://www.cnblogs.com/zhangjun1130/p/1394914.html
Copyright © 2011-2022 走看看