雪影工作室版权所有,转载请注明【http://blog.csdn.net/lina791211】
1、前言
这段时间一直在研究城市路网,某一天受不可告人的启发,决定把城市路网的地图做出来,然后模拟移动对象在路网上的运动,故本人开始了模拟地图生成的不归路。
任务要求:
(1)通过一组城市路网街道的数据(图的格式存储,demo数据下面给),把城市路网数据转换成坐标数据(其实这一步,我拿到的数据已经转换完成了,也就是说我拿到了的是平面坐标数据,而不是球面经纬度数据);
(2)根据平面坐标数据,使用Java Swing或者Visual Studio(C# WinForm)生成城市路网;
(3)支持点击某点,以此点放大;
(4)支持按中心缩小
(5)不用考虑剪枝(城市路网地图放大是存在剪枝的,那是因为附加数据太多,我的demo中只有1W多个街道,内存完全无压力)
(6)经度不考虑(这个是因为winform坐标只能是整数,无法用浮点数表示)
(7)根据移动对象的移动数据,模拟移动对象的运动。
2、数据demo
通过特殊途径拿到了一组城市路网街道的demo数据,如何拿到我就不讲了。但是可以提供另外一种获取途径。
城市路网数据获取:来点我啊,来点我啊
然后过几天就会拿到数据了。
由于我这边通过其他途径得到数据的,所以还是简要介绍一下我这边数据的格式:
(1)街道数据
0,50,8209,8769,8293,8768 1,50,8293,8768,8594,8773 2,50,8594,8773,8982,8781 3,50,8982,8781,9222,8787 4,30,8982,8781,9057,8936 5,30,9057,8936,9106,9038 6,30,9106,9038,9163,9209 7,30,9163,9209,9211,9354 8,70,9222,8787,9227,8890 9,70,9227,8890,9248,9350 10,70,9248,9350,9280,9476 11,70,9280,9476,9334,9670 12,70,9334,9670,9387,9804 13,70,9387,9804,9444,9919 14,70,9395,10233,9401,10199 15,70,9401,10199,9444,10000 16,70,9444,9919,9444,10000 17,50,8594,8773,8598,9061 18,50,8595,9495,8598,9256
简要介绍下每行的含义
street编号,限速,Start
X,Start Y,End X,End Y。
(2)移动对象数据
1 1 39:22.6 39:24.6 15537 3850 15537 3850 0 0 1 2 39:24.6 39:26.6 15537 3850 15541.5 3857 8.321658489 14.97898528 1 2 39:26.6 39:28.6 15541.5 3857 15550.3 3870.65 16.24076661 29.23337989 1 2 39:28.6 39:30.6 15550.3 3870.65 15559.4 3884.65 16.69760462 30.05568831 1 2 39:30.6 39:32.6 15559.4 3884.65 15565.5 3894.21 11.34035273 20.41263491 1 2 39:32.6 39:34.6 15565.5 3894.21 15574.4 3907.99 16.40421897 29.52759415 1 2 39:34.6 39:36.6 15574.4 3907.99 15583.5 3921.99 16.69760462 30.05568831 1 2 39:36.6 39:38.6 15583.5 3921.99 15592.5 3935.99 16.64331698 29.95797056 1 2 39:38.6 39:40.6 15592.5 3935.99 15601.6 3949.99 16.69760462 30.05568831 1 2 39:40.6 39:42.6 15601.6 3949.99 15610.6 3964 16.65172964 29.97311335 1 2 39:42.6 39:44.6 15610.6 3964 15619 3977.01 15.48612605 27.87502689 1 2 39:44.6 39:46.6 15619 3977.01 15625.7 3987.33 12.3041619 22.14749142 1 2 39:46.6 39:48.6 15625.7 3987.33 15634.7 4001.33 16.64331698 29.95797056 1 2 39:48.6 39:50.6 15634.7 4001.33 15643.7 4015.33 16.64331698 29.95797056 1 2 39:50.6 39:52.6 15643.7 4015.33 15652.8 4029.34 16.70598994 30.0707819 1 2 39:52.6 39:54.6 15652.8 4029.34 15661.8 4043.34 16.64331698 29.95797056 1 2 39:54.6 39:56.4 15661.8 4043.34 15670 4056 15.08362026 27.15051646 1 2 39:56.4 39:58.4 15670 4056 15663.1 4060.69 8.343027029 15.01744865 1 2 39:58.4 40:00.4 15663.1 4060.69 15649.7 4069.83 16.22034525 29.19662145
简要介绍下每行的含义
移动对象编号,暂无意义,开始时间,结束时间,Start X,Start Y,End X,End Y,暂无意义(其实是行驶路程),暂无意义(其实是平均速度)
3、先上demo
讲了这么久,先把路网的demo拿出来看一下。(放大两倍后的,不敢把原图搞出来,不知道是否涉密)
4、数据处理
手里拿到的路网数据,是平面坐标数据,而且起始范围特别大,用1366这样的范围根本不能忍。故要读取样本,获取X、Y的最大值、最小值,并获取区间范围,然后缩放坐标范围到1000以内就行。
(一)获取最大值最小值
实现机制很简单,读取文件,转换数据,比较大小,分别获取X与Y的最大值和最小值;
/// <summary> /// 测试使用 /// 获取坐标集合中X 、Y的最大值和最小值 /// </summary> /// <param name="strs"></param> private void getMax_Min_XY(string[] strs) { int xMin = 0; int xMax = 0; int yMin = 0; int yMax = 0; string[] s = strs[0].Split(','); xMin = Convert.ToInt32(s[2]); xMax = Convert.ToInt32(s[4]); yMin = Convert.ToInt32(s[3]); yMax = Convert.ToInt32(s[5]); if (xMin < xMax) { int temp = xMin; xMin = xMax; xMax = temp; } if (yMin < yMax) { int temp = yMin; yMin = yMax; yMax = temp; } // Console.WriteLine("\txMax \txMin \tyMax \tyMin"); for (int i = 1; i < strs.Length; i++) { Console.Write("\n"+i); string[] s1 = strs[i].Split(','); int _xMin = Convert.ToInt32(s1[2].Contains(".") ? s1[2].Substring(0, s1[2].IndexOf('.')) : s1[2]); int _xMax = Convert.ToInt32(s1[4].Contains(".") ? s1[4].Substring(0, s1[4].IndexOf('.')) : s1[4]); int _yMin = Convert.ToInt32(s1[3].Contains(".") ? s1[3].Substring(0, s1[3].IndexOf('.')) : s1[3]); int _yMax = Convert.ToInt32(s1[5].Contains(".") ? s1[5].Substring(0, s1[5].IndexOf('.')) : s1[5]); if (_xMin < xMin) xMin = _xMin; else if (_xMin > xMax) xMax = _xMin; if (_xMax < xMin) xMin = _xMax; else if (_xMax > xMax) xMax = _xMax; if (_yMin < yMin) yMin = _yMin; else if (_yMin > yMax) yMax = _yMin; if (_yMax < yMin) yMin = _yMax; else if (_yMax > yMax) yMax = _yMax; Console.Write("\t" + xMax); Console.Write("\t" + xMin); Console.Write("\t" + yMax); Console.Write("\t" + yMin); } Console.WriteLine("xMax:" + xMax); Console.WriteLine("xMin:" + xMin); Console.WriteLine("yMax:" + yMax); Console.WriteLine("yMin:" + yMin); }
使用Convert.ToInt32(s1[2].Contains(".") ? s1[2].Substring(0, s1[2].IndexOf(‘.‘)) : s1[2]);的原因是:数据中有浮点数,,在粗粒度处理下,我就暴力的舍弃了。
从而得到XY的最大和最小值:
// private int xMax =33575; // private int xMin=-10836; // private int yMax=28095; // private int yMin = -6686;
通过计算,xMax-xMin,yMax-yMin,离他们最近的最大整数分别是 90000和70000。
因此,我选择了form页面大小为900*700的。中心点是(450,350),xmin取-11000,ymin取-6800,坐标转换后需要的缩放倍率是50
private static int xmin = -11000; private static int ymin = -6800; private static int rate = 50; private int _Xc = 450; private int _Yc = 350;
这些数据都作为固定值保存下来。不能每次加载地图的时候都去处理一遍啊,是不?处理数据很慢的。
(二)转换坐标,并保存文件
转换坐标好理解,保存文件是为了下次启动的时候可以直接读转换后的数据,而不用重新转换坐标了。其实这些都是数据的预处理。
/// <summary> /// 测试使用的,根据城市路网坐标数据,生成符合当前坐标的路网坐标 /// </summary> private void ConvertStreetPoints() { string[] strs = System.IO.File.ReadAllLines("c://streets_seg.csv"); FileStream fs = new FileStream("C:\\A.txt", FileMode.Append); StreamWriter sw = new StreamWriter(fs, Encoding.Default); //Graphics g = this.pictureBox1.CreateGraphics(); for (int i = 1; i < strs.Length; i++) { string[] s1 = strs[i].Split(','); //Console.Write("\n"); // Console.WriteLine(getX(s1[2])); // Console.WriteLine(getX(s1[4]) ); // Console.WriteLine(getY(s1[3]) ); // Console.WriteLine(getY(s1[5])); //sw.Write(text); sw.Write(getX(s1[2]) + "\n"); sw.Write(getX(s1[4]) + "\n"); sw.Write(getY(s1[3]) + "\n"); sw.Write(getY(s1[5]) + "\n"); // Pen p = new Pen(Color.Red); // Point a = new Point(getX(s1[2]), getY(s1[3])); // Point b = new Point(getX(s1[4]), getY(s1[5])); // g.DrawLine(p, a, b); } sw.Close(); fs.Close(); }
里面涉及了坐标的转换,方法如下:
/// <summary> /// 转换路网坐标 /// </summary> /// <param name="x"></param> /// <returns></returns> private int getX(String x) { int tmp = Convert.ToInt32(x.Contains(".") ? x.Substring(0, x.IndexOf('.')) : x); return (tmp - xmin) / rate; } /// <summary> /// 转换路网坐标 /// </summary> /// <param name="y"></param> /// <returns></returns> private int getY(String y) { int tmp = Convert.ToInt32(y.Contains(".") ? y.Substring(0, y.IndexOf('.')) : y); return (tmp - ymin) / rate; }
处理后的坐标样式如下,每行一个数据,按照start X,end X,Start Y,End Y 进行保存的。当初脑子有点抽
385 391 311 311 391 399 311 311
okay,路网的数据就这样基本处理完成了,下面开始进行地图绘制操作了。
5、地图绘制
系统启动的时候,首先要把转换后的路网坐标数据存到内存中,(不要跟我说存神马R树啊之类的,内容太少,没必要)
private int[] ax = new int[MAX]; private int[] ay = new int[MAX]; private int[] bx = new int[MAX]; private int[] by = new int[MAX];
/// <summary> /// 初始化路网路口坐标 /// </summary> private void initPoints() { string[] strs = System.IO.File.ReadAllLines("C:\\A.txt"); for (int i = 0; i < strs.Length/4; i++) { //Console.WriteLine(strs[i]); ax[i] = Convert.ToInt32(strs[i * 4]); bx[i] = Convert.ToInt32(strs[i * 4 + 1]); ay[i] = Convert.ToInt32(strs[i * 4 + 2]); by[i] = Convert.ToInt32(strs[i * 4 + 3]); } }
然后剩下的就是A-B这样进行划线啦。so easy的!(给form添加一个paint事件)
/// <summary> /// 绘制地图主方法 /// </summary> private void repaint() { Graphics g = this.CreateGraphics(); for (int i = 1; i < ax.Length; i++) { Pen p = new Pen(Color.Red); Point a = new Point(ax[i], ay[i]); Point b = new Point(bx[i], by[i]); g.DrawLine(p, a, b); } } /// <summary> /// 绘制地图(Form的paint事件) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Form1_Paint(object sender, PaintEventArgs e) { repaint(); }
Form1_Load方法中直接调用对应的方法就行。
private void Form1_Load(object sender, EventArgs e) { string[] strs = System.IO.File.ReadAllLines("c://streets_seg.csv"); //Console.WriteLine(strs.Length); //ConvertStreetPoints(); //ConvertTripPoint(); initPoints(); // initTripPoints(); //getMax_Min_XY(strs); // }
6、地图出现
系统启动后会在1S左右把数据加载进来,然后绘制地图的图形,速度还是不错的。如果直接拿原始数据,先切割,再转换数据类型,然后缩放坐标点,这一套流程下来要耗费1分多的。
下一章讲述如何放大和缩小。
okay,本章结束!
C# 城市路网地图生成与运动模拟(一)-数据的获取,布布扣,bubuko.com