日常生活中,上班下班坐地鐵已經是常事,每當我想去某一個遠一點的地方,如果有地鐵首選就是地鐵,因為方便嘛!每次坐地鐵,我們都是憑肉眼去得出我們心中最佳的換乘方案,但是,如果對於線路較少的城市來說,這個方法是最快的,但是如果對於線路較多的城市,例如北京或者上海,十幾條線路交叉穿梭,我們可能看到都暈了,怎么坐才是時間最短路程最短的,我們要算出來不是不可以但是很麻煩,我們也可以想一想,百度地圖的地鐵換乘算法是怎么實現的,於是,閑着沒事,我就想寫一個通用的地鐵換乘查詢程序,想用計算機運算得出科學一點的換乘方案供自己參考,假設先不考慮站點間的距離差異,我們以乘坐站點數最少為最優方案,依照這個條件去編碼實現查找的算法,其實也沒用上什么高大上的算法,因為也不會哈哈,話不多說,先上效果圖:

有對應城市的線路圖(支持鼠標滾輪放大縮小):

站點智能提示:

項目結構圖:

我的開發思路:
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 - 1; 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 - 1; 69 var rang2 = coss2.Count - 1; 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 = 1 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 = 0; 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 - 1; 122 var r2 = destRange.Count - 1; 123 var r3 = transRange.Count - 1; 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 = 2, 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 - 1; 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 - 1; 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 - 1; 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 - 1; 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 = 3, 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 - 1; 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 - 1; 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 - 1; 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 - 1; 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 - 1; 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 = 4, 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 > 0) //依賴交叉線 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 - 1; 368 var rang2 = coss2.Count - 1; 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 = 1, 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 - 1; 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 - 1; 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 - 1; 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 - 1; 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 == 0 || s3_h == 0 || s4_h == 0 || 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 = 3, 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 - 1; 487 var rang2 = coss2.Count - 1; 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 = 1, 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 - 1; 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 - 1; 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 - 1; 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 - 1; 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 == 0 || s2_h == 0 || s3_h == 0 || 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 = 3, 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 > 0) //依賴交叉線 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 == -1) 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 - 1; 634 var ixh2 = ixRange2.Count - 1; 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 = 1, 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 > 0) 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 - 1; 703 var r2 = transRange.Count - 1; 704 var r3 = destRange.Count - 1; 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 > 0 && r2 > 0 && r3 > 0) 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 = 2, 721 TransferStations = new List<string>() { d1.Name, d2.Name } 722 }); 723 } 724 else if (r1 > 0 && r2 == 0 && r3 > 0) 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 = 1, 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 = 1, 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[0]; 761 x.Route = desc[2]; 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 > 2 767 || x.Route.Split(new string[] { destStation }, StringSplitOptions.RemoveEmptyEntries).Length > 2); 768 return gyResult; 769 }
因為只做了北京、上海、深圳、廣州四個城市的站點數據,所以演示只能查詢這四個城市的地鐵換乘,大家可以補充自己想要的城市的地鐵數據,格式請參照源碼中上面四個城市的xml格式。
最后附上源碼地址,https://gitee.com/herubin/subway_transfer_query_tool
參數說明:dep為始發站,dest為到達站,city為所在城市拼音,這個是專門為其他程序調用開放的接口
我在我的小博客也發布了:http://www.xiaoboke.net/admin/blog/article/26
核心方法代碼有點多,並不是有意這樣寫,正在想法子壓縮代碼,大家將就看吧(*^__^*)
有興趣的朋友可以看看,歡迎大家提出改進意見,碼個代碼不容易,各位大哥大姐的點贊是我不斷努力的動力
,謝謝!
