点的基本操作
要求
提供空间点数据文本文件,包含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("保存成功!!!");
}
}
}
实现效果如下