点的基本操作
要求
提供空间点数据文本文件,包含ID、name、X、Y四个字段信息,
1)读取数据,并且在窗口中显示点的具体位置,用实心圆绘制。
2)鼠标任意点击三个点,将点连线,用黑色笔刷绘制线。
3)鼠标点击选中其中一条线,将线颜色变为黄色。
4)移动任意一点,将新的坐标信息修改后保存到文本文件中。
思路:1、定义一个点类,存点;定义一个线类,存线数据
2、定义一个服务类,用于处理先关点操作
3、窗口实现
首先是一个点
public class Point{public int ID { get; set; }public string Name { get;set; }public float X { get; set; }public float Y { get; set; }public Point(int id, string name, float x, float y){ID = id;Name = name;X = x;Y = y;}}
再是一个线
public class Line{public List<Point> Coordinates { get; set; }public double k{ get; set; }public double b{ get; set; }public bool isselect { get; set; }public Line(List<Point> coords){var pt1 = coords[0];var pt2 = coords[1];Coordinates = coords;this.k = (pt2.Y - pt1.Y) / (pt2.X - pt1.X);this.b = pt1.Y - k * pt1.X;isselect = false;}}
再是相关服务类
public class PointService{//读取数据public List<Point> ReadToFile(string filepath){List<Point> pts = new List<Point>();StreamReader sr = new StreamReader(filepath);while (!sr.EndOfStream){var str = sr.ReadLine();var str1 = str.Split(',');pts.Add(new Point(Int32.Parse(str1[0]), str1[1], float.Parse(str1[2]), float.Parse(str1[3])));}return pts;}//保存数据public void SaveToFile(List<Point> pts, string filepath){StreamWriter sw = new StreamWriter(filepath);foreach (var pt in pts){sw.WriteLine(pt.ID+","+pt.Name+","+pt.X+","+pt.Y);}sw.Close();}//获取选择点public Point getPoint(List<Point> pts, float x, float y){Point nearestPoint = null;float minDistance = float.MaxValue;foreach (var point in pts){//这里用float.parse会报错,该方法将string转化为float,不可以在这里用float distance = (float)(Math.Sqrt(Math.Pow(x-point.X,2) + Math.Pow(y - point.Y, 2)));if (distance < minDistance){minDistance = distance;nearestPoint = point;}}// 可能需要增加一定的阈值来确认点击有效return minDistance <=4.5 ? nearestPoint : null;}// 根据给定点查找最近的线要素public Line FindNearestLineFeature(List<Line> lines, Point point){Line nearestLine = null;double minDistance = float.MaxValue;// 遍历所有线要素,计算每条线与指定点的距离foreach (var line in lines){double distance = CalculateDistance(line, point); // 假设存在一个计算距离的方法// 如果当前线距指定点的距离小于已找到的最近距离,则更新结果if (distance < minDistance){minDistance = distance;nearestLine = line;}}return nearestLine;}// 计算线要素与点之间的最小距离(实际项目中需使用GIS相关的空间分析算法)private double CalculateDistance(Line line, Point point){var pt1 = line.Coordinates[0];var pt2 = line.Coordinates[1];// 这里仅作占位符,实际应实现点到线段的最短距离算法return Math.Abs(line.k * point.X - point.Y + line.b) / Math.Sqrt(line.k * line.k + 1);}}
窗口类
public partial class Form1 : Form{public List<Point> pts = new List<Point>();//存所有点public PointService P = new PointService();//点相关服务初始化public List<Point> selectedPoints = new List<Point>();//存用于连成线的点List<Line> lines = new List<Line>(); // 存储线条对应的点集合,这个用法是真牛逼,但感觉没有Dictionary<int, bool> highlightedLines = new Dictionary<int, bool>(); // 用于记录被高亮的线条Pen blackPen = new Pen(Color.Black);Pen yellowPen = new Pen(Color.Yellow);//这里三个参数通过按钮控制,来控制picturebox中点击事件触发对象public bool isSelectPoint = false;//是否选择点public bool isSelectLine = false;//是否选择线public bool isMovePoint = false;//是否移动线//临时存点public Point CPoint=null;//选择的移动点public Form1(){InitializeComponent();}public void drawpicture(){}private void button1_Click(object sender, EventArgs e){if (openFileDialog1.ShowDialog() == DialogResult.OK){pts = P.ReadToFile(openFileDialog1.FileName);foreach (var pt in pts){richTextBox1.Text += pt.ID + "\t" + pt.Name + "\t" + pt.X + "\t" + pt.Y + "\n";}// 触发PictureBox的重绘pictureBox1.Invalidate();}else{MessageBox.Show("读取失败");}}private void pictureBox1_Click(object sender, EventArgs e){}//这个是picture自带的事件,该事件触发需要调用对应函数,否则不会主动刷新private void pictureBox1_Paint(object sender, PaintEventArgs e){foreach (var point in pts){e.Graphics.FillEllipse(Brushes.Black, new RectangleF(point.X, point.Y, 5, 5));}foreach (var line in lines){var start = line.Coordinates[0];var end = line.Coordinates[1];DrawLine(e.Graphics, start, end, line.isselect? yellowPen : blackPen);//这个用法可以记录一下}}private void DrawLine(Graphics g, Point start, Point end, Pen pen){g.DrawLine(pen, new PointF(start.X, start.Y), new PointF(end.X, end.Y));}private void button2_Click(object sender, EventArgs e){isSelectPoint = true;isSelectLine = false;isMovePoint = false;}private void pictureBox1_MouseClick(object sender, MouseEventArgs e){try{//启动找点if (isSelectPoint && !isSelectLine){var pt = P.getPoint(pts, e.X, e.Y);if (pt != null){selectedPoints.Add(pt);}if (selectedPoints.Count == 3){for (int i = 0; i < selectedPoints.Count; i++){List<Point> ptsList = new List<Point>();ptsList.Add(selectedPoints[i]);ptsList.Add(selectedPoints[(i + 1) % 3]);Line line = new Line(ptsList);lines.Add(line);}}pictureBox1.Invalidate();}//找线if (!isSelectPoint && isSelectLine){int i= lines.IndexOf(P.FindNearestLineFeature(lines, new Point(0, "t", e.X, e.Y)));lines[i].isselect = true;pictureBox1.Invalidate();}}catch (Exception exception){}}private void button3_Click(object sender, EventArgs e){isSelectPoint = false;isSelectLine = true;isMovePoint = false;}private void pictureBox1_MouseDown(object sender, MouseEventArgs e){if (isMovePoint){CPoint = P.getPoint(pts, e.X, e.Y);}}private void pictureBox1_MouseMove(object sender, MouseEventArgs e){if (isMovePoint){if (CPoint != null){CPoint.Y = e.Y;CPoint.X = e.X;pts[pts.IndexOf(CPoint)] = CPoint;pictureBox1.Invalidate();}}}private void pictureBox1_MouseUp(object sender, MouseEventArgs e){if (isMovePoint){CPoint = null;}}private void button4_Click(object sender, EventArgs e){isSelectPoint = false;isSelectLine = false;isMovePoint = true;}private void button5_Click(object sender, EventArgs e){if (saveFileDialog1.ShowDialog()==DialogResult.OK){P.SaveToFile(pts,saveFileDialog1.FileName);MessageBox.Show("保存成功!!!");}}}
实现效果如下