为了使用ArcMobile实现量测功能,LZ自定义了一个MapGraphicLayer用于绘图,代码如下:
using System.Drawing; using ESRI.ArcGIS.Mobile; using ESRI.ArcGIS.Mobile.Geometries; namespace LandInspections { public class MeasureLayer : MapGraphicLayer { /// Defines a symbol class instance for displaying point private Symbol drawSymbol; /// Defines a CoordinateCollection instance to store custom features private CoordinateCollection coordinateCollection = new CoordinateCollection(); /// Get or set coordinate collection stored in custom graphic layer public CoordinateCollection Coordinates { get { return coordinateCollection; } set { coordinateCollection = value; } } /// Initializes a new instance of custom graphic layer public MeasureLayer() : base("MeasureLayer") { drawSymbol = new Symbol( new PointPaintOperation( Color.Green, 3, 0, Color.LightGreen, 100, 25, PointPaintStyle.Circle)); } /// Draw method being called after adding to map protected override void Draw(Display display) { //return if display is null if (display == null) return; //return if drawing is cancelled if (display.DrawingCanceled) return; drawSymbol.DrawArea(display, coordinateCollection); } protected override void Dispose(bool disposing) { //Dispose symbol implementing IDisposible try { if (disposing) { if (drawSymbol != null) drawSymbol.Dispose(); drawSymbol = null; coordinateCollection = null; } } finally { base.Dispose(disposing); } } } }
在“量测”按钮所在的窗体代码中,添加如下代码:
/// <summary> /// MapGraphicLayer的坐标点 /// </summary> private CoordinateCollection coords = new CoordinateCollection(); private BtnMeasure_Click(object sender, EventArgs e) { if (this.txtMeasure.Visible)//txtMeasure是用来显示量测结果的控件 { this.txtMeasure.Visible = false; this.coords.Clear(); this.map1.MapGraphicLayers.Remove(this.measureLayer); this._mapAction = MyMapAction.None; } else { this.txtMeasure.Text = "量测: 长度:0.000 米 面积:0.000 平方米"; this.measureLayer = new MeasureLayer(); this.measureLayer.Coordinates = this.coords; this.map1.MapGraphicLayers.Add(this.measureLayer); this._mapAction == MyMapAction.Measure; this.txtMeasure.Visible = true; } } private void map1_MouseUp(object sender, MapMouseEventArgs e) { if (this._mapAction == MyMapAction.Measure) { Coordinate coord = this.map1.ToMap(e.X, e.Y); this.coords.Add(coord); Polyline line = new Polyline(this.measureLayer.Coordinates); Polygon area = new Polygon(this.measureLayer.Coordinates); this.txtMeasure.Text = String.Format("量测: 长度:{0} 米 面积:{1} 平方米", line.GetLength().ToString("f3"), area.GetArea().ToString("f3")); this.map1.Refresh(); } }
运行后,发现顺时针点击屏幕的绘图结果正确,但是逆时针点击屏幕时绘图结果错误。
检查后发现是因为ESRI.ArcGIS.Mobile.Geometries.CoordinateCollection类的Add方法对点集的顺序进行了自动调整,在this.coords.Add(coord)时点集的顺序已经被改变了。
添加点1:CoordinateCollection中点集为[1]。
添加点2:CoordinateCollection中点集为[1,2,1]。
添加点3:CoordinateCollection中点集为[1,3,2,1]。
添加点4:CoordinateCollection中点击为[1,4,2,3,1]。
为解决这一问题,LZ在程序中定义一个List<Coordinate> coords,用来存储正确顺序的点集,在自定义的MapGraphicLayer中添加一个SetCoordinate方法,用来保证以顺时针方向添加节点到CoordinateCollection中。代码如下:
public void SetCoordinate(System.Collections.Generic.List<Coordinate> coords) { if (coords == null || coords.Count < 1) return; if (this.coordinateCollection == null) this.coordinateCollection = new CoordinateCollection();
this.coordinateCollection.Clear();
//若为顺时针,则直接赋值 for (int i = 0; i < coords.Count; i++) { this.coordinateCollection.Add(new Coordinate(coords[i].X, coords[i].Y)); if (this.coordinateCollection.IsCounterClockwise == true) break; } //若为逆时针,则反向以顺时针方式将点集添加到CoordinateCollection中 if (this.coordinateCollection.IsCounterClockwise == true) { this.coordinateCollection.Clear(); this.coordinateCollection.Add(new Coordinate(coords[0].X, coords[0].Y)); for (int i = coords.Count - 1; i > 0; i--) { this.coordinateCollection.Add(new Coordinate(coords[i].X, coords[i].Y)); } } }
相应的,“量测”功能所在窗体的代码也需要进行相应的修改,代码如下:
/// <summary> /// MapGraphicLayer的坐标点 /// </summary> private List<Coordinate> coords = new List<Coordinate>(); private BtnMeasure_Click(object sender, EventArgs e) { if (this.txtMeasure.Visible)//txtMeasure是用来显示量测结果的控件 { this.txtMeasure.Visible = false; this.coords.Clear(); this.map1.MapGraphicLayers.Remove(this.measureLayer); this._mapAction = MyMapAction.None; } else { this.txtMeasure.Text = "量测: 长度:0.000 米 面积:0.000 平方米"; this.measureLayer = new MeasureLayer(); this.measureLayer.Coordinates.Clear();
this.map1.MapGraphicLayers.Add(this.measureLayer); this._mapAction == MyMapAction.Measure; this.txtMeasure.Visible = true; } } private void map1_MouseUp(object sender, MapMouseEventArgs e) { if (this._mapAction == MyMapAction.Measure) { Coordinate coord = this.map1.ToMap(e.X, e.Y); this.coords.Add(coord);
this.measureLayer.SetCoordinate(this.coords); Polyline line = new Polyline(this.measureLayer.Coordinates); Polygon area = new Polygon(this.measureLayer.Coordinates); this.txtMeasure.Text = String.Format("量测: 长度:{0} 米 面积:{1} 平方米", line.GetLength().ToString("f3"), area.GetArea().ToString("f3")); this.map1.Refresh(); } }
经过测试,这种方式是可行的。之后大概是因为无聊,也可能是为了验证自己的想法,又分别测试了完全反向插入节点和完全正向插入节点的情况,结果出乎意料,竟然都是正确的???
完全反向插入节点:即将[1,2,3,4]以[1,4,3,2]的顺序插入,无论节点顺序是顺时针还是逆时针。测试代码如下:
public void SetCoordinate(System.Collections.Generic.List<Coordinate> coords) { if (coords == null || coords.Count < 1) return; if (this.coordinateCollection == null) this.coordinateCollection = new CoordinateCollection();
this.coordinateCollection.Clear(); //反向插入节点 this.coordinateCollection.Add(new Coordinate(coords[0].X, coords[0].Y)); for (int i = coords.Count - 1; i > 0; i--) { this.coordinateCollection.Add(new Coordinate(coords[i].X, coords[i].Y)); } }
完全正向插入节点:即将[1,2,3,4]以[1,2,3,4]的顺序插入,无论节点顺序是顺时针还是逆时针。测试代码如下:
public void SetCoordinate(System.Collections.Generic.List<Coordinate> coords) { if (coords == null || coords.Count < 1) return; if (this.coordinateCollection == null) this.coordinateCollection = new CoordinateCollection(); this.coordinateCollection.Clear(); for (int i = 0; i < coords.Count; i++) { this.coordinateCollection.Add(new Coordinate(coords[i].X, coords[i].Y)); } }
最后的事实证明,只要每次绘图时,将所有的节点重新添加一次即可,于是LZ最终选择了完全正向插入节点的方式。