利用ArcEngine实现距离量测,面积量测的功能已很简单,相信众多的ArcGIS爱好者都能写. 但单纯的实现功能总觉得欠什么.本人喜欢改代码,喜欢优化代码,在原有的功能基础上总喜欢"润色",使之更好看.前一整子在玩skyline时看到 skyline 的测距功能能实时显示量测的距离,于是联想到之前自己用C#+ArcEngine 写的测距功能.何不也优化一下自己代码? 想到就做到.最近手有点痒,算练练手.
废话少说,先附上效果图:
说明:
1.本功能的特点在于在量测的过称当中实时显示量测距离,并将结果实时显示在测距上方.在显示窗体上也显示量测的总距离.
要点:
1. INewLineFeedback 负责画线;
2. 在OnMouseDown事件中计算量测距离,并向GraphicsContainer添加线和节点的Element;
3.在OnMouseUp事件中实时计算距离随鼠标移动后产生的新距离.
4. 将节点,量测值,线的element都分别存入到节点组和轨迹线组(IGroupElement),即用IGroupElement同一管理这些element; 最后将这些groupelement又添加到一个总的groupelement.这样做的目的在于好控制这些element,特别是启动新的量测或取消量测功能时可以控制这些element,而不必去用IGraphicsContainer.DeleteAllElements来清除这些element,使用IGraphicsContainer.DeleteAllElements会将所有的element删除.
5. 量测值element 为ITextElement, 由于他停靠在轨迹线的上方,因此需要对量测值element进行一定角度的旋转,旋转角度由轨迹线的方向角决定.
6. 绘制element之后的刷新问题也是比较重要的. 刷新范围一定要控制好,太小了,添加的element显示不出来,太大了,浪费. (实在不好控制就刷新整个extent吧,呵呵)
核心代码如下:
1. TrackLine类
TrackLine
//=================================功能测距==========================
//描述:实时显示测量距离,节点位置,总长度
//编程: Jin 开发时间:2009.8.10-2009.8.11
//特点:实时计算量测距离.
//缺点: 由于采用element做为显示内容,地图进行放大缩小操作后并不能很好
// 控制量测值element和轨迹线element之间的距离间隔.
//===================================================================
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.Geometry;
using System.Windows.Forms;
namespace MhGis.GisTool.Pb_Toolbar
{
class TrackLine : BaseTool
{
private IHookHelper m_hookHelper = null;
private INewLineFeedback m_NewLineFeedback = null;
private IPointCollection m_ptColl; //记录节点
private MeasureMsgInfo _MsgInfo = null;
private IPolyline m_TraceLine = null; //完整的轨迹线
//
private IGroupElement m_Elements = null; //用于保存包含此功能产生的所有Element
private IGroupElement m_TraceElement = null; //测距轨迹线
private IGroupElement m_VertexElement = null; //结点
private IGroupElement m_LabelElement = null; // 距离标记
public TrackLine()
{
//
// TODO: Define values for the public properties
//
base.m_category = ""; //localizable text
base.m_caption = ""; //localizable text
base.m_message = "This should work in ArcMap/MapControl/PageLayoutControl"; //localizable text
base.m_toolTip = ""; //localizable text
base.m_name = ""; //unique id, non-localizable (e.g. "MyCategory_MyTool")
try
{
//
// TODO: change resource name if necessary
//
string bitmapResourceName = GetType().Name + ".bmp";
base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
base.m_cursor = new System.Windows.Forms.Cursor(GetType(), GetType().Name + ".cur");
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
}
}
public MeasureMsgInfo MsgInfo
{
set
{
_MsgInfo = value;
_MsgInfo.FormClosing += new FormClosingEventHandler(msgInfo_FromClosing);
}
}
#region Overriden Class Methods
/// <summary>
/// Occurs when this tool is created
/// </summary>
/// <param name="hook">Instance of the application</param>
public override void OnCreate(object hook)
{
try
{
m_hookHelper = new HookHelperClass();
m_hookHelper.Hook = hook;
if (m_hookHelper.ActiveView == null)
{
m_hookHelper = null;
}
}
catch
{
m_hookHelper = null;
}
if (m_hookHelper == null)
base.m_enabled = false;
else
base.m_enabled = true;
// TODO: Add other initialization code
}
void Init()
{
//初始化
m_Elements = new GroupElementClass();
m_TraceElement = new GroupElementClass();
m_VertexElement = new GroupElementClass();
m_LabelElement = new GroupElementClass();
//初始化,并添加到GraphicsContainer
IGraphicsContainer g = m_hookHelper.ActiveView as IGraphicsContainer;
g.AddElement(m_Elements as IElement, 0);
g.AddElement(m_TraceElement as IElement, 0);
g.AddElement(m_VertexElement as IElement, 0);
g.AddElement(m_LabelElement as IElement, 0);
//添加到m_Elements中
g.MoveElementToGroup(m_VertexElement as IElement, m_Elements);
g.MoveElementToGroup(m_LabelElement as IElement, m_Elements);
g.MoveElementToGroup(m_TraceElement as IElement, m_Elements);
}
/// <summary>
/// Occurs when this tool is clicked
/// </summary>
public override void OnClick()
{
Init();
}
void msgInfo_FromClosing(object sender, FormClosingEventArgs e)
{
DeleteAllElements();
_MsgInfo = null;
//throw new Exception("The method or operation is not implemented.");
}
public override void OnMouseDown(int Button, int Shift, int X, int Y)
{
if (Button == 2)
return;
IPoint pt = m_hookHelper.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(X, Y);
IGraphicsContainer g = m_hookHelper.ActiveView.GraphicsContainer;
IEnvelope pEnvBounds = null;
//获取上一次轨迹线的范围,以便确定刷新范围
try
{
if (m_TraceLine != null)
{
m_TraceLine.QueryEnvelope(pEnvBounds);
pEnvBounds.Expand(4, 4, true); //矩形框向四周扩大4倍(大于2倍就行),目的是为了保证有充足的刷新区域
}
else
pEnvBounds = m_hookHelper.ActiveView.Extent;
}
catch
{
pEnvBounds = m_hookHelper.ActiveView.Extent;
}
#region 启动画线
if (m_NewLineFeedback == null)
{
//移除element
RemoveElements();
//刷新
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);
Application.DoEvents();
m_NewLineFeedback = new NewLineFeedbackClass();
m_NewLineFeedback.Display = m_hookHelper.ActiveView.ScreenDisplay;
//必须先得到symbol,后设置symbol
ISimpleLineSymbol simpleLineSymbol = m_NewLineFeedback.Symbol as ISimpleLineSymbol;
simpleLineSymbol.Style = esriSimpleLineStyle.esriSLSDot;
simpleLineSymbol.Width = 1;
simpleLineSymbol.Color = TransColorToAEColor(Color.Blue);
m_NewLineFeedback.Start(pt);
}
else
{
m_NewLineFeedback.AddPoint(pt);
}
if (m_ptColl == null)
{
m_ptColl = new PolylineClass();
}
//记录节点
object obj = Type.Missing;
m_ptColl.AddPoint(pt, ref obj, ref obj);
#endregion
#region 绘制结点
try
{
IElement vertexElement = CreateElement(pt);
//
g = m_hookHelper.ActiveView as IGraphicsContainer;
//g.AddElement(vertexElement, 0);
//g.MoveElementToGroup(vertexElement, m_VertexElement);
m_VertexElement.AddElement(vertexElement);
//刷新
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, vertexElement, pEnvBounds);
}
catch
{ }
#endregion
try
{
if (m_ptColl.PointCount >= 2)
{
IPoint fromPt = m_ptColl.get_Point(m_ptColl.PointCount - 2); //倒数第二个点
IPoint toPt = m_ptColl.get_Point(m_ptColl.PointCount - 1); //最后第一个点
ILine line = new LineClass();
line.PutCoords(fromPt, toPt);
#region 绘制轨迹线
try
{
object missing = Type.Missing;
ISegmentCollection segColl = new PolylineClass();
segColl.AddSegment(line as ISegment, ref missing, ref missing);
IElement traceElement = CreateElement(segColl as IPolyline);
//
g = m_hookHelper.ActiveView as IGraphicsContainer;
//g.AddElement(traceElement, 0);
//g.MoveElementToGroup(traceElement, m_TraceElement);
m_TraceElement.AddElement(traceElement);
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, traceElement, pEnvBounds);
}
catch
{ }
#endregion
#region 计算单线的长度,并将结果显示在单线中点偏上上面
try
{
double angle = line.Angle;
if ((angle > (Math.PI / 2) && angle < (Math.PI)) || (angle > -Math.PI && angle < -(Math.PI / 2))) // 大于90度小于等于180
angle += Math.PI;
//标注点Y值偏移量
double d_OffsetY = m_hookHelper.ActiveView.ScreenDisplay.DisplayTransformation.FromPoints(9);
//标注点
double d_CenterX = (fromPt.X + toPt.X) / 2;
double d_CenterY = (fromPt.Y + toPt.Y) / 2 + d_OffsetY; //向上偏移
IPoint labelPt = new PointClass();
labelPt.PutCoords(d_CenterX, d_CenterY);
ITextElement txtElement = CreateTextElement(line.Length.ToString("0.00"));
IElement labelelement = txtElement as IElement;
labelelement.Geometry = labelPt;
object oElement = (object)labelelement;
//根据角度旋转
TransformByRotate(ref oElement, labelPt, angle);
////添加到GraphicsContainer
//g.AddElement(labelelement, 0);
////移到m_LabelElement组中
//g.MoveElementToGroup(labelelement, m_LabelElement);
//添加到组
m_LabelElement.AddElement(labelelement);
//刷新
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, labelelement, pEnvBounds);
}
catch
{ }
#endregion
}
}
catch
{ }
}
public override void OnMouseMove(int Button, int Shift, int X, int Y)
{
if (m_NewLineFeedback == null)
return;
IPoint pt = m_hookHelper.ActiveView.ScreenDisplay.DisplayTransformation.ToMapPoint(X, Y);
m_NewLineFeedback.MoveTo(pt);
if (m_ptColl.PointCount == 0)
return;
double d_Total = 0;
double d_segment = 0;
IPoint lastPt = m_ptColl.get_Point(m_ptColl.PointCount - 1);
ILine line = new LineClass();
line.PutCoords(lastPt, pt);
//节距离
d_segment = line.Length;
_MsgInfo.Segment = d_segment;
try
{
IPolyline polyline = m_ptColl as IPolyline;
if (polyline.IsEmpty)
{
d_Total = d_segment;
}
else
{
d_Total = polyline.Length + d_segment;
}
}
catch
{
}
//赋值给总长度
_MsgInfo.Total = d_Total;
}
public override void OnDblClick()
{
if (m_NewLineFeedback == null)
return;
//绘制线与多边形几何图形时,双击结束绘制
try
{
m_TraceLine = m_NewLineFeedback.Stop();
if (m_TraceLine == null)
return;
}
catch
{ }
finally
{
Recycle();
}
}
#endregion
//回收
public void Recycle()
{
m_NewLineFeedback = null;
m_ptColl.RemovePoints(0, m_ptColl.PointCount);
m_ptColl = null;
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, m_hookHelper.ActiveView.Extent);
}
/// <summary>
/// 从组中删除所有元素
/// </summary>
/// <param name="groupElement"></param>
void RemoveElementFromGroupElement(IGroupElement groupElement)
{
if (groupElement == null || groupElement.ElementCount == 0)
return;
try
{
IGraphicsContainer g = m_hookHelper.ActiveView.GraphicsContainer;
for (int index = 0; index < groupElement.ElementCount; index++)
{
IElement tmp_Ele = groupElement.get_Element(index);
if (tmp_Ele is IGroupElement)
RemoveElementFromGroupElement(tmp_Ele as IGroupElement);
else
{
try
{
groupElement.DeleteElement(tmp_Ele);
}
catch
{
}
finally
{
tmp_Ele = null;
}
}
}
//groupElement.ClearElements();
}
catch
{ }
finally
{
//刷新
IEnvelope pEnvBounds = null;
//获取上一次轨迹线的范围,以便确定刷新范围
try
{
if (m_TraceLine != null)
{
m_TraceLine.QueryEnvelope(pEnvBounds);
pEnvBounds.Expand(4, 4, true); //矩形框向四周扩大4倍(大于2倍就行),目的是为了保证有充足的刷新区域
}
else
pEnvBounds = m_hookHelper.ActiveView.Extent;
}
catch
{
pEnvBounds = m_hookHelper.ActiveView.Extent;
}
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, pEnvBounds);
}
}
/// <summary>
/// 移除节点,标注和轨迹线Element
/// </summary>
void RemoveElements()
{
try
{
//刷新一次
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, m_hookHelper.ActiveView.Extent);
IGraphicsContainer g = m_hookHelper.ActiveView.GraphicsContainer;
#region 1-new
//RemoveElementFromGroupElement(m_Elements);
#endregion
#region 2
m_LabelElement.ClearElements();
m_VertexElement.ClearElements();
m_TraceElement.ClearElements();
#endregion
}
catch
{
}
finally
{
//刷新一次
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, m_hookHelper.ActiveView.Extent);
}
}
/// <summary>
/// 删除所有与此相关的元素
/// </summary>
public void DeleteAllElements()
{
//m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);
IGraphicsContainer g = m_hookHelper.ActiveView.GraphicsContainer;
RemoveElementFromGroupElement(m_Elements);
try
{
g.DeleteElement(m_Elements as IElement);
}
catch
{ }
finally
{
m_TraceElement = null;
m_LabelElement = null;
m_VertexElement = null;
m_Elements = null;
//最后再刷新一次
m_hookHelper.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);
}
}
/// <summary>
/// 将系统颜色转换为IColor
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
ESRI.ArcGIS.Display.IColor TransColorToAEColor(Color color)
{
IRgbColor rgb = new RgbColorClass();
rgb.RGB = color.R + color.G * 256 + color.B * 65536;
return rgb as IColor;
}
/// <summary>
/// 按指定的角度旋转
/// </summary>
/// <param name="obj"></param>
/// <param name="originPt"></param>
/// <param name="rotate"></param>
void TransformByRotate(ref object obj, IPoint originPt, double rotate)
{
if (obj == null && originPt == null)
return;
try
{
ITransform2D transform2D = obj as ITransform2D;
if (transform2D == null)
return;
transform2D.Rotate(originPt, rotate);
}
catch
{ }
}
#region 创建Element
/// <summary>
/// 创建一个TextElement
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
ITextElement CreateTextElement(string text)
{
//创建一个TextSymbol
ITextSymbol txtSymbol = new TextSymbolClass();
//设置字体
Font dispFont = new Font("Arial", 10, FontStyle.Regular);
txtSymbol.Font = (stdole.IFontDisp)ESRI.ArcGIS.ADF.COMSupport.OLE.GetIFontDispFromFont(dispFont);
//设置属性
txtSymbol.Color = TransColorToAEColor(Color.Red); //颜色
//创建一个TextElement
ITextElement txtElement = new TextElementClass();
txtElement.Symbol = txtSymbol;
txtElement.Text = text;
return txtElement;
}
/// <summary>
/// 绘制几何图形
/// </summary>
/// <param name="geoType"></param>
/// <param name="geometry"></param>
/// <returns></returns>
ESRI.ArcGIS.Carto.IElement CreateElement(ESRI.ArcGIS.Geometry.IGeometry geometry)
{
IElement element = null;
try
{
switch (geometry.GeometryType)
{
case esriGeometryType.esriGeometryPolyline://Polyline线
ISimpleLineSymbol simpleLineSymbol = m_NewLineFeedback.Symbol as ISimpleLineSymbol;
ILineElement lineElement = new LineElementClass();
lineElement.Symbol = simpleLineSymbol as ILineSymbol;
element = lineElement as IElement;
element.Geometry = geometry;
break;
case esriGeometryType.esriGeometryPoint:
//设置结点符号
IRgbColor pRGB = new RgbColorClass();
pRGB.Red = 255;
pRGB.Green = 0;
pRGB.Blue = 0;
ISimpleMarkerSymbol pSimpleMarkSymbol = new SimpleMarkerSymbolClass();
pSimpleMarkSymbol.Color = pRGB as IColor;
pSimpleMarkSymbol.Size = 2;
pSimpleMarkSymbol.Style = esriSimpleMarkerStyle.esriSMSSquare;
IMarkerElement pMarkerElement = new MarkerElementClass();
pMarkerElement.Symbol = pSimpleMarkSymbol as IMarkerSymbol;
element = pMarkerElement as IElement;
element.Geometry = geometry as IGeometry;
break;
}
}
catch
{ }
return element;
}
#endregion
}
}