日常生活中,上班下班坐地铁已经是常事,每当我想去某一个远一点的地方,如果有地铁首选就是地铁,因为方便嘛!每次坐地铁,我们都是凭肉眼去得出我们心中最佳的换乘方案,但是,如果对于线路较少的城市来说,这个方法是最快的,但是如果对于线路较多的城市,例如北京或者上海,十几条线路交叉穿梭,我们可能看到都晕了,怎么坐才是时间最短路程最短的,我们要算出来不是不可以但是很麻烦,我们也可以想一想,百度地图的地铁换乘算法是怎么实现的,于是,闲着没事,我就想写一个通用的地铁换乘查询程序,想用计算机运算得出科学一点的换乘方案供自己参考,假设先不考虑站点间的距离差异,我们以乘坐站点数最少为最优方案,依照这个条件去编码实现查找的算法,其实也没用上什么高大上的算法,因为也不会哈哈,话不多说,先上效果图:

有对应城市的线路图(支持鼠标滚轮放大缩小):

站点智能提示:

项目结构图:

我的开发思路:

1、采用xml存储站点数据,如下:

2、代码中使用集合初始化线路数据

  1. /// <summary>
  2. /// 初始化地铁线路数据
  3. /// </summary>
  4. /// <param name="city">城市</param>
  5. public static void InitSubwayLine(CityEnum city)
  6. {
  7. if (AllSubwayLines != null && AllSubwayLines.Any() && _currentCity == city) return;
  8. _currentCity = city;
  9. var xmlName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "xml/" + city.ToString() + ".xml");
  10. _doc = XDocument.Load(xmlName);
  11. AllSubwayLines = _doc.Root.Elements().Select(x =>
  12. {
  13. var line = new SubwayLine
  14. {
  15. No = x.Attribute("No").Value,
  16. Name = x.Attribute("Name").Value,
  17. IsRound = x.Attribute("IsRound") == null ? false : bool.Parse(x.Attribute("IsRound").Value),
  18. Stations = x.Elements().Select((y, i) => new Station
  19. {
  20. Index = i,
  21. Name = y.Attribute("Name").Value,
  22. LineNo = x.Attribute("No").Value,
  23. CanTransfer = y.Attribute("CanTransfer") == null ? false : bool.Parse(y.Attribute("CanTransfer").Value),
  24. TransferNo = y.Attribute("TransferNo") == null ? null : y.Attribute("TransferNo").Value
  25. }).ToList()
  26. };
  27. var translines = line.GetTransStations().Select(z => z.TransferNo.Split(',')).ToList();
  28. foreach (var transline in translines)
  29. {
  30. foreach (var li in transline)
  31. {
  32. line.TransferLines.Add(li);
  33. }
  34. }
  35. line.TransferLines = line.TransferLines.Distinct().ToList();
  36. return line;
  37. }).ToList();
  38. }

3、分多种情况进行处理,核心代码(代码有点长,我也在想方法浓缩代码,还望各位看官耐心(┬_┬)):

  1. /// <summary>
  2. /// 获取地铁乘车信息
  3. /// </summary>
  4. /// <param name="depStation">出发站</param>
  5. /// <param name="destStation">到达站</param>
  6. /// <param name="city">所在城市</param>
  7. /// <returns>返回各种换乘方案</returns>
  8. public static List<QueryResult> GetRideSubwayInfo(string depStation, string destStation, CityEnum city)
  9. {
  10. InitSubwayLine(city);
  11. if (string.IsNullOrWhiteSpace(depStation) || string.IsNullOrWhiteSpace(destStation)
  12. || !AllSubwayLines.Exists(x => x.Stations.Exists(y => y.Name.Equals(depStation)))
  13. || !AllSubwayLines.Exists(x => x.Stations.Exists(y => y.Name.Equals(destStation))))
  14. return null;//出发站或到达站在线路上不存在!
  15.  
  16. //各种换乘提示
  17. //同一条线路
  18. var msg_oneline = "在{0}【{1}】上车,经过{2}站到达目的站【{3}】。\r\n具体线路为:\r\n(出发){4}(到达)\r\n总搭乘站点数:{5}\r\n";
  19. //换乘1次
  20. var msg_transOnce = "在{0}【{1}】上车,经过{2}站在【{3}】下车,换乘{4},经过{5}站到达目的站【{6}】。\r\n具体线路为:\r\n(出发){7}(此处换乘{4})-->{8}(到达)\r\n总搭乘站点数:{9}\r\n";
  21. //换乘2次
  22. var msg_transTwice = "在{0}【{1}】上车,经过{2}站在【{3}】下车,换乘{4},经过{5}站在【{6}】下车,换乘{7},经过{8}站到达目的站【{9}】。\r\n具体线路为:\r\n(出发){10}(此处换乘{4})-->{11}(此处换乘{7})-->{12}(到达)\r\n总搭乘站点数:{13}\r\n";
  23. //换乘3次
  24. var msg_transThreetimes = "在{0}【{1}】上车,经过{2}站在【{3}】下车,换乘{4},经过{5}站在【{6}】下车,换乘{7},经过{8}站在【{9}】下车,换乘{10},经过{11}站到达目的站【{12}】。"
  25. + "\r\n具体线路为:\r\n(出发){13}(此处换乘{4})-->{14}(此处换乘{7})-->{15}(此处换乘{10})-->{16}(到达)\r\n总搭乘站点数:{17}\r\n";
  26. //换乘4次
  27. var msg_transFourtimes = "在{0}【{1}】上车,经过{2}站在【{3}】下车,换乘{4},经过{5}站在【{6}】下车,换乘{7},经过{8}站在【{9}】下车,换乘{10},经过{11}站在【{12}】下车,换乘{13},经过{14}站到达目的站【{15}】。"
  28. + "\r\n具体线路为:\r\n(出发){16}(此处换乘{4})-->{17}(此处换乘{7})-->{18}(此处换乘{10})-->{19}(此处换乘{13})-->{20}(到达)\r\n总搭乘站点数:{21}\r\n";
  29.  
  30. //保存各种换乘方案
  31. var result = new List<QueryResult>();
  32. //第一步:先查找始发站和到达站在哪一条线路
  33. var afterDepLines = GetAcrossLines(depStation);
  34. var afterDestLines = GetAcrossLines(destStation);
  35. //根据同一条线和不同线路展开分析
  36. if (IsSameLine(depStation, destStation))
  37. {
  38. #region 同一条线路
  39. var commLines = afterDepLines.Where(x => afterDestLines.Select(y => y.No).Contains(x.No)).ToList();
  40. //判断线路是否相同,相同直接计算站点距离
  41. var depIndex = GetIndexOnLine(depStation, commLines.First());
  42. var destIndex = GetIndexOnLine(destStation, commLines.First());
  43. var crossStations = commLines.First().Stations.Between(depIndex, destIndex).Select(x => x.Name).ToList();
  44. var range = crossStations.Count - ;
  45. if (depIndex > destIndex) crossStations.Reverse();
  46. var rs = msg_oneline.FormatTo(commLines.First().ToString(), depStation, range, destStation,
  47. crossStations.ToJoinString(), range);
  48. result.Add(new QueryResult() { Description = rs, Range = range });
  49. #endregion
  50. }
  51. else
  52. {
  53. #region 不同线路
  54. if (!IsTransferStation(depStation) && !IsTransferStation(destStation))//如果始发站和终点站都不是换乘站,则表示始发站和到达站都是只有一条线路通过
  55. {
  56. if (afterDepLines.First().IsIntersect(afterDestLines.First()))
  57. {
  58. #region 如果两条线路交叉,一定有换乘站点
  59. var clist = GetAcrossStations(afterDepLines.First(), afterDestLines.First()).Select(x => x.Name).ToList();
  60. var i = GetIndexOnLine(depStation, afterDepLines.First());
  61. var j = GetIndexOnLine(clist.First(), afterDepLines.First());
  62. var k = GetIndexOnLine(destStation, afterDestLines.First());
  63. var l = GetIndexOnLine(clist.First(), afterDestLines.First());
  64. var coss1 = afterDepLines.First().Stations.Between(i, j).Select(x => x.Name).ToList();
  65. var coss2 = afterDestLines.First().Stations.Between(k, l).Select(x => x.Name).ToList();
  66. if (i > j) coss1.Reverse();
  67. if (k < l) coss2.Reverse();
  68. var rang1 = coss1.Count - ;
  69. var rang2 = coss2.Count - ;
  70. var h = rang1 + rang2; //站点数
  71. var rs = msg_transOnce.FormatTo(afterDepLines.First().ToString(), depStation, rang1, clist.First(),
  72. afterDestLines.First().ToString(), rang2, destStation,
  73. coss1.ToJoinString(), coss2.Where(x => x != clist.First()).ToJoinString(), h);
  74. result.Add(new QueryResult()
  75. {
  76. Description = rs,
  77. Range = h,
  78. TransferStations = new List<string>() { clist.First() },
  79. TransferTimes =
  80. });
  81. #endregion
  82. }
  83. else
  84. {
  85. #region 不交叉,需要通过第三条线路换乘,即多次换乘
  86. var depSta = GetStation(depStation);
  87. var destSta = GetStation(destStation);
  88. //找出两条线路的可换乘站点,找出可换乘相同线路的站点
  89. var trans1 = afterDepLines.First().GetTransStations();
  90. var trans2 = afterDestLines.First().GetTransStations();
  91. var trans3 = new List<Station>();
  92. var trans4 = new List<Station>();
  93. var expets = trans1.Join(trans2, x => x.TransferNo, y => y.TransferNo, (x, y) =>
  94. {
  95. trans3.Add(x);
  96. trans4.Add(y);
  97. return x.Name + "---" + y.Name;
  98. }).ToList();
  99. if (expets.Any())
  100. {
  101. #region 两次换乘
  102. //trans3.Count和trans4.Count必定相等
  103. //计算最短距离,列出所有换乘方案
  104. for (var i = ; i < trans3.Count; i++)
  105. {
  106. var tranLine = GetLine(trans3[i].TransferNo);
  107. //获取这两个站点在此线路的索引
  108. var ix1 = depSta.Index;
  109. var ix2 = destSta.Index;
  110. var iix1 = GetIndexOnLine(trans3[i].Name, depSta.LineNo);
  111. var iix2 = GetIndexOnLine(trans4[i].Name, destSta.LineNo);
  112. var tx1 = GetIndexOnLine(trans3[i].Name, tranLine);
  113. var tx2 = GetIndexOnLine(trans4[i].Name, tranLine);
  114.  
  115. var depRange = afterDepLines.First().Stations.Between(ix1, iix1).Select(x => x.Name).ToList();
  116. var destRange = afterDestLines.First().Stations.Between(ix2, iix2).Select(x => x.Name).ToList();
  117. var transRange = tranLine.Stations.Between(tx1, tx2).Select(x => x.Name).ToList();
  118. if (ix1 > iix1) depRange.Reverse();
  119. if (ix2 < iix2) destRange.Reverse();
  120. if (tx1 > tx2) transRange.Reverse();
  121. var r1 = depRange.Count - ;
  122. var r2 = destRange.Count - ;
  123. var r3 = transRange.Count - ;
  124. var r = r1 + r2 + r3;
  125. var rs = msg_transTwice.FormatTo(afterDepLines.First().ToString(), depStation, r1,
  126. trans3[i].Name,
  127. tranLine.ToString(), r3, trans4[i].Name, afterDestLines.First().ToString(), r2,
  128. destStation, depRange.ToJoinString(),
  129. transRange.Where(x => !x.IsSame(trans3[i].Name) && !x.IsSame(trans4[i].Name)).ToJoinString(),
  130. destRange.ToJoinString(), r);
  131. result.Add(new QueryResult()
  132. {
  133. Description = rs,
  134. Range = r,
  135. TransferTimes = ,
  136. TransferStations = new List<string>() { trans3[i].Name, trans4[i].Name }
  137. });
  138. }
  139. #endregion
  140. }
  141. #region 查找3次以上换乘的可能结果,寻求最短距离
  142. var trlines1 = afterDepLines.First().TransferLines.Select(GetLine).ToList();
  143. var trlines2 = afterDestLines.First().TransferLines.Select(GetLine).ToList();
  144. var destss = new List<Station>();
  145.  
  146. #region 换乘3次
  147. foreach (var depline in trlines1)
  148. {
  149. foreach (var destline in trlines2)
  150. {
  151. var ss = destline.GetAcrossStations(depline);
  152. if (!ss.Any()) continue; //3次换乘
  153. var slist1 = afterDepLines.First().GetAcrossStations(depline);
  154. if (!slist1.Any()) continue;
  155. var s1 = slist1.GetClosestStation(depSta.Name);
  156. var s1_ix1 = depSta.Index;
  157. var s1_ix2 = s1.Index;
  158. var s1_range =
  159. afterDepLines.First()
  160. .Stations.Between(s1_ix1, s1_ix2)
  161. .Select(x => x.Name)
  162. .ToList();
  163. var s1_h = s1_range.Count - ;
  164. if (s1_ix1 > s1_ix2) s1_range.Reverse();
  165.  
  166. var s2_ix1 = GetIndexOnLine(s1.Name, depline);
  167. var s2_ix2 = GetIndexOnLine(ss.First().Name, depline);
  168. var s2_range = depline.Stations.Between(s2_ix1, s2_ix2).Select(x => x.Name).ToList();
  169. var s2_h = s2_range.Count - ;
  170. if (s2_ix1 > s2_ix2) s2_range.Reverse();
  171.  
  172. var slist3 = destline.GetAcrossStations(afterDestLines.First());
  173. if (!slist3.Any()) continue;
  174. var s3 = slist3.GetClosestStation(ss.First().Name);
  175. var s3_ix1 = s3.Index;
  176. var s3_ix2 = ss.First().Index;
  177. var s3_range = destline.Stations.Between(s3_ix1, s3_ix2).Select(x => x.Name).ToList();
  178. var s3_h = s3_range.Count - ;
  179. if (s3_ix1 < s3_ix2) s3_range.Reverse();
  180.  
  181. var s4_ix1 = GetIndexOnLine(s3.Name, afterDestLines.First());
  182. var s4_ix2 = destSta.Index;
  183. var s4_range =
  184. afterDestLines.First()
  185. .Stations.Between(s4_ix1, s4_ix2)
  186. .Select(x => x.Name)
  187. .ToList();
  188. var s4_h = s4_range.Count - ;
  189. if (s4_ix1 > s4_ix2) s4_range.Reverse();
  190.  
  191. var h = s1_h + s2_h + s3_h + s4_h;
  192. var rs = msg_transThreetimes.FormatTo(afterDepLines.First().ToString(), depStation,
  193. s1_h, s1.Name,
  194. depline.ToString(), s2_h, ss.First().Name,
  195. GetLine(ss.First().LineNo).ToString(), s3_h, s3.Name,
  196. afterDestLines.First().ToString(), s4_h, destStation, s1_range.ToJoinString(),
  197. s2_range.Where(x => x != s1.Name).ToJoinString(),
  198. s3_range.Where(x => x != ss.First().Name).ToJoinString(),
  199. s4_range.Where(x => x != s3.Name).ToJoinString(), h);
  200. result.Add(new QueryResult()
  201. {
  202. Description = rs,
  203. Range = h,
  204. TransferTimes = ,
  205. TransferStations =
  206. new List<string>()
  207. {
  208. s1.Name,
  209. ss.First().Name,
  210. s3.Name
  211. }
  212. });
  213. destss.AddRange(ss);
  214. }
  215. }
  216. #endregion
  217.  
  218. if (!destss.Any()) //换乘4次
  219. {
  220. #region 换乘4次
  221. foreach (var depline in trlines1)
  222. {
  223. foreach (var destline in trlines2)
  224. {
  225. var deptrlines =
  226. depline.TransferLines.Where(x => x != afterDepLines.First().No)
  227. .Select(GetLine)
  228. .ToList();
  229. foreach (var line in deptrlines)
  230. {
  231. var s1 = line.GetAcrossStations(destline);
  232. if (!s1.Any()) continue; //4次换乘
  233. var trlist1 = afterDepLines.First().GetAcrossStations(depline);
  234. if (!trlist1.Any()) continue;
  235. var tr1 = trlist1.GetClosestStation(depSta.Name);
  236. var s1_ix1 = depSta.Index;
  237. var s1_ix2 = tr1.Index;
  238. var s1_range =
  239. afterDepLines.First()
  240. .Stations.Between(s1_ix1, s1_ix2)
  241. .Select(x => x.Name)
  242. .ToList();
  243. var h1 = s1_range.Count - ;
  244. if (s1_ix1 > s1_ix2) s1_range.Reverse();
  245.  
  246. var trlist2 = GetLine(tr1.TransferNo).GetAcrossStations(line);
  247. if (!trlist2.Any()) continue;
  248. var tr2 = trlist2.GetClosestStation(tr1.Name);
  249. var s2_ix1 = GetIndexOnLine(tr1.Name, depline);
  250. var s2_ix2 = tr2.Index;
  251. var s2_range =
  252. depline.Stations.Between(s2_ix1, s2_ix2)
  253. .Select(x => x.Name)
  254. .ToList();
  255. var h2 = s2_range.Count - ;
  256. if (s2_ix1 > s2_ix2) s2_range.Reverse();
  257.  
  258. var s3_ix1 = GetIndexOnLine(tr2.Name, line);
  259. var s3_ix2 = s1.First().Index;
  260. var s3_range =
  261. line.Stations.Between(s3_ix1, s3_ix2)
  262. .Select(x => x.Name)
  263. .ToList();
  264. var h3 = s3_range.Count - ;
  265. if (s3_ix1 > s3_ix2) s3_range.Reverse();
  266.  
  267. var trlist3 = destline.GetAcrossStations(afterDestLines.First());
  268. if (!trlist3.Any()) continue;
  269. var tr3 = trlist3.GetClosestStation(s1.First().Name);
  270. var s4_ix1 = GetIndexOnLine(s1.First().Name, destline);
  271. var s4_ix2 = tr3.Index;
  272. var s4_range =
  273. destline.Stations.Between(s4_ix1, s4_ix2)
  274. .Select(x => x.Name)
  275. .ToList();
  276. var h4 = s4_range.Count - ;
  277. if (s4_ix1 > s4_ix2) s4_range.Reverse();
  278.  
  279. var s5_ix1 = GetIndexOnLine(tr3.Name, afterDestLines.First());
  280. var s5_ix2 = destSta.Index;
  281. var s5_range =
  282. afterDestLines.First()
  283. .Stations.Between(s5_ix1, s5_ix2)
  284. .Select(x => x.Name)
  285. .ToList();
  286. var h5 = s5_range.Count - ;
  287. if (s5_ix1 > s5_ix2) s5_range.Reverse();
  288. var h = h1 + h2 + h3 + h4 + h5;
  289. var rs =
  290. msg_transFourtimes.FormatTo(afterDepLines.First().ToString(),
  291. depStation, h1, tr1.Name,
  292. depline.ToString(), h2, tr2.Name,
  293. line.ToString(), h3, s1.First().Name,
  294. destline.ToString(), h4, tr3.Name,
  295. afterDestLines.First().ToString(), h5, destStation,
  296. s1_range.ToJoinString(),
  297. s2_range.Where(x => x != tr1.Name).ToJoinString(),
  298. s3_range.Where(x => x != tr2.Name).ToJoinString(),
  299. s4_range.Where(x => x != tr2.Name && x != s1.First().Name).ToJoinString(),
  300. s5_range.Where(x => x != tr3.Name).ToJoinString(), h);
  301. result.Add(new QueryResult()
  302. {
  303. Description = rs,
  304. Range = h,
  305. TransferTimes = ,
  306. TransferStations =
  307. new List<string>()
  308. {
  309. tr1.Name,
  310. tr2.Name,
  311. s1.First().Name,
  312. tr3.Name
  313. }
  314. });
  315. destss.AddRange(s1);
  316. }
  317. }
  318. }
  319. #endregion
  320. }
  321. if (!destss.Any())//换乘4次以上
  322. {
  323.  
  324. }
  325. #endregion
  326. #endregion
  327. }
  328. }
  329. else //始发站和到达站有其中一个是换乘站
  330. {
  331. //找出到达站经过的路线和始发站所在路线的交叉线路
  332. var crossLines = GetAcrossLines(depStation, destStation);
  333. //分三种情况:1、始发站不是换乘站而到达站是换乘站;2、始发站是换乘站而到达站不是换乘站;3、始发站和到达站都是换乘站,根据情况展开分析
  334. if (!IsTransferStation(depStation) && IsTransferStation(destStation))
  335. {
  336. #region 情况1:始发站不是换乘站而到达站是换乘站
  337. if (crossLines.Count > ) //依赖交叉线
  338. {
  339. var listTrans = new List<Station>();
  340. foreach (var line in crossLines)
  341. {
  342. //找出每条交叉线换乘到始发站线路的所有换乘站
  343. var ss = line.GetTransStations()
  344. .Where(x => x.TransferNo.IsSame(afterDepLines.First().No))
  345. .ToList();
  346. listTrans.AddRange(ss);
  347. }
  348. var depIx = GetIndexOnLine(depStation, afterDepLines.First());
  349. var tranStas =
  350. listTrans.Select(
  351. s => new { sta = s, ix = GetIndexOnLine(s.Name, afterDepLines.First()) })
  352. .ToList();
  353. foreach (var sta in tranStas)
  354. {
  355. var destIx = GetIndexOnLine(destStation, sta.sta.LineNo);
  356. var tranIx = GetIndexOnLine(sta.sta.Name, sta.sta.LineNo);
  357. var coss1 =
  358. afterDepLines.First()
  359. .Stations.Between(depIx, sta.ix)
  360. .Select(x => x.Name)
  361. .ToList();
  362. var coss2 =
  363. GetLine(sta.sta.LineNo)
  364. .Stations.Between(destIx, tranIx)
  365. .Select(x => x.Name)
  366. .ToList();
  367. var rang1 = coss1.Count - ;
  368. var rang2 = coss2.Count - ;
  369. var h = rang1 + rang2; //站点数
  370. if (depIx > sta.ix) coss1.Reverse();
  371. if (destIx < tranIx) coss2.Reverse();
  372.  
  373. var rs = msg_transOnce.FormatTo(afterDepLines.First().ToString(), depStation, rang1,
  374. sta.sta.Name,
  375. GetLine(sta.sta.LineNo).ToString(), rang2, destStation,
  376. coss1.ToJoinString(), coss2.Where(x => x != sta.sta.Name).ToJoinString(), h);
  377. result.Add(new QueryResult()
  378. {
  379. Description = rs,
  380. Range = h,
  381. TransferTimes = ,
  382. TransferStations = new List<string>() { sta.sta.Name }
  383. });
  384. }
  385. }
  386. //查找其他换乘可能
  387. var depSta = GetStation(depStation);
  388. var trlinesDep = afterDepLines.First().TransferLines.Select(GetLine).ToList();
  389. foreach (var depline in afterDestLines)
  390. {
  391. var trlineItems = depline.TransferLines.Select(GetLine).ToList();
  392. foreach (var iline in trlineItems)
  393. {
  394. foreach (var destline in trlinesDep)
  395. {
  396. var ss = destline.GetAcrossStations(iline);
  397. if (!ss.Any()) continue; //3次换乘
  398. var slist1 = afterDepLines.First().GetAcrossStations(destline);
  399. if (!slist1.Any()) continue;
  400. var s1 = slist1.GetClosestStation(depStation);
  401. var s1_ix1 = depSta.Index;
  402. var s1_ix2 = s1.Index;
  403.  
  404. var s1_range =
  405. afterDepLines.First()
  406. .Stations.Between(s1_ix1, s1_ix2)
  407. .Select(x => x.Name)
  408. .ToList();
  409. var s1_h = s1_range.Count - ;
  410. if (s1_ix1 > s1_ix2) s1_range.Reverse();
  411.  
  412. var s2 = ss.GetClosestStation(s1.Name);
  413. var s2_ix1 = GetIndexOnLine(s1.Name, destline);
  414. var s2_ix2 = GetIndexOnLine(s2.Name, destline);
  415. var s2_range = destline.Stations.Between(s2_ix1, s2_ix2).Select(x => x.Name).ToList();
  416. var s2_h = s2_range.Count - ;
  417. if (s2_ix1 > s2_ix2) s2_range.Reverse();
  418.  
  419. var slist3 = iline.GetAcrossStations(depline);
  420. if (!slist3.Any()) continue;
  421. var s3 = slist3.GetClosestStation(s2.Name);
  422. var s3_ix1 = s3.Index;
  423. var s3_ix2 = GetIndexOnLine(s2.Name, iline);
  424. var s3_range = iline.Stations.Between(s3_ix1, s3_ix2).Select(x => x.Name).ToList();
  425. var s3_h = s3_range.Count - ;
  426. if (s3_ix1 < s3_ix2) s3_range.Reverse();
  427.  
  428. var s4_ix1 = GetIndexOnLine(s3.Name, depline);
  429. var s4_ix2 = GetIndexOnLine(destStation, depline);
  430. var s4_range = depline.Stations.Between(s4_ix1, s4_ix2)
  431. .Select(x => x.Name)
  432. .ToList();
  433. var s4_h = s4_range.Count - ;
  434. if (s4_ix1 > s4_ix2) s4_range.Reverse();
  435.  
  436. var h = s1_h + s2_h + s3_h + s4_h;
  437. if (s2_h == || s3_h == || s4_h == || destline.No.IsSame(iline.No)) continue;
  438. var rs = msg_transThreetimes.FormatTo(afterDepLines.First().ToString(),
  439. depStation,
  440. s1_h, s1.Name,
  441. destline.ToString(), s2_h, s2.Name,
  442. iline.ToString(), s3_h, s3.Name,
  443. depline.ToString(), s4_h, destStation, s1_range.ToJoinString(),
  444. s2_range.Where(x => x != s1.Name).ToJoinString(),
  445. s3_range.Where(x => x != s2.Name).ToJoinString(),
  446. s4_range.Where(x => x != s3.Name).ToJoinString(), h);
  447. result.Add(new QueryResult()
  448. {
  449. Description = rs,
  450. Range = h,
  451. TransferTimes = ,
  452. TransferStations =
  453. new List<string>()
  454. {
  455. s1.Name,
  456. s2.Name,
  457. s3.Name
  458. }
  459. });
  460. }
  461. }
  462. }
  463. #endregion
  464. }
  465. if (IsTransferStation(depStation) && !IsTransferStation(destStation))
  466. {
  467. #region 情况2:始发站是换乘站而到达站不是换乘站
  468. var transLines =
  469. afterDepLines.Where(
  470. x =>
  471. x.Stations.Exists(
  472. y => y.CanTransfer && y.TransferNo.Contains(afterDestLines.First().No)))
  473. .ToList();
  474. if (transLines.Any())
  475. {
  476. var clist =
  477. GetAcrossStations(transLines.First(), afterDestLines.First())
  478. .Select(x => x.Name)
  479. .ToList();
  480. var i = GetIndexOnLine(depStation, transLines.First());
  481. var j = GetIndexOnLine(clist.First(), transLines.First());
  482. var k = GetIndexOnLine(destStation, afterDestLines.First());
  483. var l = GetIndexOnLine(clist.First(), afterDestLines.First());
  484. var coss1 = transLines.First().Stations.Between(i, j).Select(x => x.Name).ToList();
  485. var coss2 = afterDestLines.First().Stations.Between(k, l).Select(x => x.Name).ToList();
  486. var rang1 = coss1.Count - ;
  487. var rang2 = coss2.Count - ;
  488. var h = rang1 + rang2; //站点数
  489. if (i > j) coss1.Reverse();
  490. if (k < l) coss2.Reverse();
  491.  
  492. var rs = msg_transOnce.FormatTo(transLines.First().ToString(), depStation, rang1,
  493. clist.First(),
  494. afterDestLines.First().ToString(), rang2, destStation,
  495. coss1.ToJoinString(), coss2.Where(x => x != clist.First()).ToJoinString(), h);
  496. result.Add(new QueryResult()
  497. {
  498. Description = rs,
  499. Range = h,
  500. TransferTimes = ,
  501. TransferStations = new List<string>() { clist.First() }
  502. });
  503. }
  504. //寻找其他换乘可能
  505. var destSta = GetStation(destStation);
  506. var trlinesDest = afterDestLines.First().TransferLines.Select(GetLine).ToList();
  507. foreach (var depline in afterDepLines)
  508. {
  509. var trlineItems = depline.TransferLines.Select(GetLine).ToList();
  510. foreach (var iline in trlineItems)
  511. {
  512. foreach (var destline in trlinesDest)
  513. {
  514. var ss = iline.GetAcrossStations(destline);
  515. if (!ss.Any()) continue; //3次换乘
  516. var slist1 = depline.GetAcrossStations(iline);
  517. if (!slist1.Any()) continue;
  518. var s1 = slist1.GetClosestStation(depStation);
  519. var s1_ix1 = GetIndexOnLine(depStation, depline);
  520. var s1_ix2 = s1.Index;
  521. var s1_range =
  522. depline.Stations.Between(s1_ix1, s1_ix2)
  523. .Select(x => x.Name)
  524. .ToList();
  525. var s1_h = s1_range.Count - ;
  526. if (s1_ix1 > s1_ix2) s1_range.Reverse();
  527.  
  528. var s2 = ss.GetClosestStation(s1.Name);
  529. var s2_ix1 = GetIndexOnLine(s1.Name, iline);
  530. var s2_ix2 = GetIndexOnLine(s2.Name, iline);
  531. var s2_range = iline.Stations.Between(s2_ix1, s2_ix2).Select(x => x.Name).ToList();
  532. var s2_h = s2_range.Count - ;
  533. if (s2_ix1 > s2_ix2) s2_range.Reverse();
  534.  
  535. var slist3 = destline.GetAcrossStations(afterDestLines.First());
  536. if (!slist3.Any()) continue;
  537. var s3 = slist3.GetClosestStation(ss.First().Name);
  538. var s3_ix1 = s3.Index;
  539. var s3_ix2 = GetIndexOnLine(s2.Name, destline);
  540. var s3_range = destline.Stations.Between(s3_ix1, s3_ix2).Select(x => x.Name).ToList();
  541. var s3_h = s3_range.Count - ;
  542. if (s3_ix1 < s3_ix2) s3_range.Reverse();
  543.  
  544. var s4_ix1 = GetIndexOnLine(s3.Name, afterDestLines.First());
  545. var s4_ix2 = destSta.Index;
  546. var s4_range =
  547. afterDestLines.First()
  548. .Stations.Between(s4_ix1, s4_ix2)
  549. .Select(x => x.Name)
  550. .ToList();
  551. var s4_h = s4_range.Count - ;
  552. if (s4_ix1 > s4_ix2) s4_range.Reverse();
  553.  
  554. var h = s1_h + s2_h + s3_h + s4_h;
  555. if (s1_h == || s2_h == || s3_h == || iline.No.IsSame(destline.No)) continue;
  556. var rs = msg_transThreetimes.FormatTo(depline.ToString(), depStation,
  557. s1_h, s1.Name,
  558. iline.ToString(), s2_h, s2.Name,
  559. destline.ToString(), s3_h, s3.Name,
  560. afterDestLines.First().ToString(), s4_h, destStation,
  561. s1_range.ToJoinString(),
  562. s2_range.Where(x => x != s1.Name).ToJoinString(),
  563. s3_range.Where(x => x != s2.Name).ToJoinString(),
  564. s4_range.Where(x => x != s3.Name).ToJoinString(), h);
  565. result.Add(new QueryResult()
  566. {
  567. Description = rs,
  568. Range = h,
  569. TransferTimes = ,
  570. TransferStations =
  571. new List<string>()
  572. {
  573. s1.Name,
  574. s2.Name,
  575. s3.Name
  576. }
  577. });
  578. }
  579. }
  580. }
  581. #endregion
  582. }
  583. if (IsTransferStation(depStation) && IsTransferStation(destStation))
  584. {
  585. #region 情况3:始发站和到达站都是换乘站
  586. if (crossLines.Count > ) //依赖交叉线
  587. {
  588. var transStations = GetClosestStation(depStation, true);
  589. if (
  590. !transStations.Exists(
  591. x =>
  592. crossLines.Exists(y => y.Stations.Exists(z => x.TransferNo.Split(',').Intersect(z.LineNo.Split(',')).Any()))))
  593. {
  594. transStations = new List<Station>();
  595. afterDepLines.ForEach(x =>
  596. {
  597. var ctrans = x.GetTransStations();
  598. var ctrans2 =
  599. ctrans.Where(
  600. y =>
  601. crossLines.Exists(
  602. z => z.Stations.Exists(zz => y.TransferNo.Split(',').Intersect(zz.LineNo.Split(',')).Any())))
  603. .ToList();
  604. transStations.AddRange(ctrans2);
  605. });
  606. }
  607. var transLine =
  608. afterDestLines.Where(
  609. x =>
  610. x.Stations.Exists(y => transStations.Exists(z => z.Name.IsSame(y.Name))))
  611. .ToList();
  612. var intersStas =
  613. transStations.Where(
  614. x =>
  615. transLine.Exists(
  616. y =>
  617. y.Stations.Exists(
  618. z => z.Name.IsSame(x.Name) && x.TransferNo.Split(',').Intersect(z.LineNo.Split(',')).Any())))
  619. .ToList();
  620. foreach (var line in transLine)
  621. {
  622. //分别获取换乘站在换乘线上的索引
  623. foreach (var t in intersStas)
  624. {
  625. var ix = GetIndexOnLine(destStation, line); //目的站在换乘线上的索引
  626. var iix = GetIndexOnLine(t.Name, line); //换乘站在换乘线上的索引
  627. if (iix == -) continue;
  628. var ix2 = GetIndexOnLine(depStation, t.LineNo); //始发站在换乘站所在线路上的索引
  629. var iix2 = GetIndexOnLine(t.Name, t.LineNo); //换乘站在始发站所在线路上的索引
  630.  
  631. var ixRange = line.Stations.Between(ix, iix).Select(x => x.Name).ToList();
  632. var ixRange2 = GetLine(t.LineNo).Stations.Between(ix2, iix2).Select(x => x.Name).ToList();
  633. var ixh = ixRange.Count - ;
  634. var ixh2 = ixRange2.Count - ;
  635. var h = ixh + ixh2;
  636. if (ix < iix) ixRange.Reverse();
  637. if (ix2 > iix2) ixRange2.Reverse();
  638. var rs = msg_transOnce.FormatTo(GetLine(t.LineNo).ToString(), depStation, ixh2, t.Name,
  639. line.ToString(), ixh, destStation,
  640. ixRange2.ToJoinString(),
  641. ixRange.Where(x => !x.IsSame(t.Name)).ToJoinString(), h);
  642. result.Add(new QueryResult()
  643. {
  644. Description = rs,
  645. Range = h,
  646. TransferTimes = ,
  647. TransferStations = new List<string>() { t.Name }
  648. });
  649. }
  650. }
  651. }
  652. #endregion
  653. }
  654. }
  655. #endregion
  656. }
  657.  
  658. //查找其他可能性
  659. #region 深度挖掘其他方案
  660. //找出经过始发站和到达站的线路中共同穿过的公共线路,寻找换乘方案
  661. var connLines = GetCommonLines(depStation, destStation);
  662. if (connLines.Count > )
  663. {
  664. var transDep = new List<Station>();
  665. var transDest = new List<Station>();
  666. foreach (var depLine in afterDepLines)
  667. {
  668. //在经过始发站的线路中的站点中找到可换乘到公共线路的站点
  669. var trans = depLine.GetTransStations()
  670. .Where(x => x.Name != depStation && x.Name != destStation && connLines.Exists(y => x.TransferNo.Contains(y.No)))
  671. .ToList();
  672. transDep.AddRange(trans);
  673. }
  674. foreach (var destLine in afterDestLines)
  675. {
  676. //在经过到达站的线路中的站点中找到可换乘到公共线路的站点
  677. var trans = destLine.GetTransStations()
  678. .Where(x => x.Name != depStation && x.Name != destStation && connLines.Exists(y => x.TransferNo.Contains(y.No)))
  679. .ToList();
  680. transDest.AddRange(trans);
  681. }
  682. foreach (var d1 in transDep)
  683. {
  684. foreach (var d2 in transDest)
  685. {
  686. //找出交叉站点,即可换乘同一条公共线路的站点
  687. var inters = d1.TransferNo.Split(',').Intersect(d2.TransferNo.Split(',')).ToList();
  688. if (!inters.Any() || d1.Name.IsSame(d2.Name)) continue;
  689. var tranLine = GetLine(inters.First());
  690. var depLine = GetLine(d1.LineNo);
  691. var destLine = GetLine(d2.LineNo);
  692. var ix1 = GetIndexOnLine(depStation, depLine);
  693. var ix2 = GetIndexOnLine(d1.Name, depLine);
  694. var iix1 = GetIndexOnLine(d1.Name, tranLine);
  695. var iix2 = GetIndexOnLine(d2.Name, tranLine);
  696. var iiix1 = GetIndexOnLine(d2.Name, destLine);
  697. var iiix2 = GetIndexOnLine(destStation, destLine);
  698.  
  699. var depRange = depLine.Stations.Between(ix1, ix2).Select(x => x.Name).ToList();
  700. var transRange = tranLine.Stations.Between(iix1, iix2).Select(x => x.Name).ToList();
  701. var destRange = destLine.Stations.Between(iiix1, iiix2).Select(x => x.Name).ToList();
  702. var r1 = depRange.Count - ;
  703. var r2 = transRange.Count - ;
  704. var r3 = destRange.Count - ;
  705. var r = r1 + r2 + r3;
  706. if (ix1 > ix2) depRange.Reverse();
  707. if (iix1 > iix2) transRange.Reverse();
  708. if (iiix1 > iiix2) destRange.Reverse();
  709. string rs;
  710. if (r1 > && r2 > && r3 > )
  711. {
  712. rs = msg_transTwice.FormatTo(depLine.ToString(), depStation, r1, d1.Name, tranLine.ToString(), r2,
  713. d2.Name, destLine.ToString(), r3, destStation, depRange.ToJoinString(),
  714. transRange.Where(x => !x.IsSame(d1.Name)).ToJoinString(),
  715. destRange.Where(x => !x.IsSame(d1.Name) && !x.IsSame(d2.Name)).ToJoinString(), r);
  716. result.Add(new QueryResult()
  717. {
  718. Description = rs,
  719. Range = r,
  720. TransferTimes = ,
  721. TransferStations = new List<string>() { d1.Name, d2.Name }
  722. });
  723. }
  724. else if (r1 > && r2 == && r3 > )
  725. {
  726. rs = msg_transOnce.FormatTo(GetLine(inters.First()).ToString(), depStation, r1, d2.Name,
  727. destLine.ToString(), r3, destStation,
  728. depRange.ToJoinString(),
  729. destRange.Where(x => !x.IsSame(d2.Name)).ToJoinString(), r);
  730. result.Add(new QueryResult()
  731. {
  732. Description = rs,
  733. Range = r,
  734. TransferTimes = ,
  735. TransferStations = new List<string>() { d2.Name }
  736. });
  737. }
  738. else
  739. {
  740. rs = msg_transOnce.FormatTo(depLine.ToString(), depStation, r1, d1.Name, tranLine.ToString(), r2,
  741. destStation, depRange.ToJoinString(),
  742. transRange.Where(x => !x.IsSame(d1.Name)).ToJoinString(), r);
  743. result.Add(new QueryResult()
  744. {
  745. Description = rs,
  746. Range = r,
  747. TransferTimes = ,
  748. TransferStations = new List<string>() { d1.Name }
  749. });
  750. }
  751. }
  752. }
  753. }
  754. #endregion
  755.  
  756. //重新组织数据
  757. result.ForEach(x =>
  758. {
  759. var desc = x.Description.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
  760. x.Suggestion = desc[];
  761. x.Route = desc[];
  762. });
  763. //去除重复方案,保留一个
  764. var gyResult = result.GroupBy(x => x.Route).Select(x => x.First()).ToList();
  765. //移除逗逼方案,例如:A-B-A-C
  766. gyResult.RemoveAll(x => x.Route.Split(new string[] { depStation }, StringSplitOptions.RemoveEmptyEntries).Length >
  767. || x.Route.Split(new string[] { destStation }, StringSplitOptions.RemoveEmptyEntries).Length > );
  768. return gyResult;
  769. }

因为只做了北京、上海、深圳、广州四个城市的站点数据,所以演示只能查询这四个城市的地铁换乘,大家可以补充自己想要的城市的地铁数据,格式请参照源码中上面四个城市的xml格式。

最后附上源码地址,https://gitee.com/herubin/subway_transfer_query_tool

api接口地址:http://www.xiaoboke.net/api/subway/q?dep=%E6%B5%B7%E7%8F%A0%E5%B9%BF%E5%9C%BA&dest=%E5%A4%A7%E5%89%A7%E9%99%A2&city=guangzhou

参数说明:dep为始发站,dest为到达站,city为所在城市拼音,这个是专门为其他程序调用开放的接口

我在我的小博客也发布了:http://www.xiaoboke.net/admin/blog/article/26

核心方法代码有点多,并不是有意这样写,正在想法子压缩代码,大家将就看吧(*^__^*)

有兴趣的朋友可以看看,欢迎大家提出改进意见,码个代码不容易,各位大哥大姐的点赞是我不断努力的动力​,谢谢!

用C#开发的一个通用的地铁换乘查询工具的更多相关文章

  1. 基于 React 开发了一个 Markdown 文档站点生成工具

    Create React Doc 是一个使用 React 的 markdown 文档站点生成工具.就像 create-react-app 一样,开发者可以使用 Create React Doc 来开发 ...

  2. Java反射结合JDBC写的一个通用DAO

    以前写反射只是用在了与设计模式的结合上,并没有考虑到反射可以与DAO结合.也是一个偶然的机会,被正在上培训的老师点到这个问题,才考虑到这个可能性,于是上网参考各种代码,然后自己动手开发了一个通用DAO ...

  3. Linux C编程学习之开发工具3---多文件项目管理、Makefile、一个通用的Makefile

    GNU Make简介 大型项目的开发过程中,往往会划分出若干个功能模块,这样可以保证软件的易维护性. 作为项目的组成部分,各个模块不可避免的存在各种联系,如果其中某个模块发生改动,那么其他的模块需要相 ...

  4. iOS开发:代码通用性以及其规范 第二篇(猜想iOS中实现TableView内部设计思路(附代码),以类似的思想实现一个通用的进度条)

    在iOS开发中,经常是要用到UITableView的,我曾经思考过这样一个问题,为什么任何种类的model放到TableView和所需的cell里面,都可以正常显示?而我自己写的很多view却只是能放 ...

  5. 封装一个通用递归算法,使用TreeIterator和TreeMap来简化你的开发工作。

    在实际工作中,你肯定会经常的对树进行遍历,并在树和集合之间相互转换,你会频繁的使用递归. 事实上,这些算法在逻辑上都是一样的,因此可以抽象出一个通用的算法来简化工作. 在这篇文章里,我向你介绍,我封装 ...

  6. 利用RBAC模型实现一个通用的权限管理系统

    本文主要描述一个通用的权限系统实现思路与过程.也是对此次制作权限管理模块的总结. 制作此系统的初衷是为了让这个权限系统得以“通用”.就是生产一个web系统通过调用这个权限系统(生成的dll文件), 就 ...

  7. 在开发第一个Android应用之前需要知道的5件事:

    你能否详细讲述一下,在开发Android应用过程中每一阶段要用到的技能和编程语言? 建立一个Android应用程序可以归结为两个主要技能/语言:Java和Android系统.Java是Android的 ...

  8. 基于ASP.Net Core开发一套通用后台框架记录-(数据库设计(权限模块))

    写在前面 本系列博客是本人在学习的过程中搭建学习的记录,如果对你有所帮助那再好不过.如果您有发现错误,请告知我,我会第一时间修改. 前期我不会公开源码,我想是一点点敲代码,不然复制.粘贴那就没意思了. ...

  9. 基于ASP.Net Core开发一套通用后台框架记录-(总述)

    写在前面 本系列博客是本人在学习的过程中搭建学习的记录,如果对你有所帮助那再好不过.如果您有发现错误,请告知我,我会第一时间修改. 前期我不会公开源码,我想是一点点敲代码,不然复制.粘贴那就没意思了. ...

随机推荐

  1. Jlink 烧写Uboot

    第一章 Hi3531_SDK_Vx.x.x.x版本升级操作说明 如果您是首次安装本SDK,请直接参看第2章. 第二章首次安装SDK 1.Hi3531 SDK包位置 在"Hi3531_V100 ...

  2. windows2003服务器系统日志:查看电脑远程登录记录

    控制面板>>管理工具>>事件查看器>>选择安全性再点工具栏目中查看>>筛选>>事件ID填528进行过滤,时间你看是多久,双击查看之后就可以找 ...

  3. 【javascript】jquery jsonp跨越请求

    <html> <head> <meta charset="utf-8"> <title></title> <scr ...

  4. INF 右键安装驱动以及卸载

    INF 右键安装驱动以及卸载 之前写过一篇文章是关于INF文件详解的,大家可以参看INF文件详解,这次写的是关于INF右键安装,这样比较方便.卸载的话也是一句话,可以大大减少安装时间: 先将INF文件 ...

  5. hdu5820 Lights

    主席树 但是能够想到题解的做法很难 #include <stdio.h> #include <string.h> #include <vector> #includ ...

  6. 前端测试框架对比(js单元测试框架对比)

    前端测试框架对比(js单元测试框架对比) 本文主要目的在于横评业界主流的几款前端框架,顺带说下相关的一些内容. 测试分类 通常应用会有 单元测试(Unit tests) 和 功能测试(Function ...

  7. 洛谷P4003 无限之环(infinityloop)(网络流,费用流)

    洛谷题目传送门 题目 题目描述 曾经有一款流行的游戏,叫做 Infinity Loop,先来简单的介绍一下这个游戏: 游戏在一个 n ∗ m 的网格状棋盘上进行,其中有些小方格中会有水管,水管可能在格 ...

  8. [UVAlive4297]First Knight

    题面在这里 题意 给定一个\(n\times m\)的格网,从\((1,1)\)出发,每一格\((i,j)\)往上下左右移动的概率已经给出,询问到达\((n,m)\)的期望步数 数据范围 \[n,m\ ...

  9. 【Spring源码分析】配置文件读取流程

    前言 Spring配置文件读取流程本来是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一起的,这两天在看Spring自定义标签的时候,感觉对Spri ...

  10. Node.js 部署免费/自动续订 HTTPS

    随着互联网快速发展,互联网信息安全越来越受到大家重视,HTTPS 应该是近两年各大厂商都在尽力普及的技术之一.国内大厂基本上已经全面普及了 HTTPS. 本文首发于我的个人网站:听说 - https: ...