用C#開發的一個通用的地鐵換乘查詢工具


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

3.png

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

2.png

站點智能提示:

5.png

項目結構圖:

7.png

我的開發思路:

1、采用xml存儲站點數據,如下:

6.png

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

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

核心方法代碼有點多,並不是有意這樣寫,正在想法子壓縮代碼,大家將就看吧(*^__^*) 

有興趣的朋友可以看看,歡迎大家提出改進意見,碼個代碼不容易,各位大哥大姐的點贊是我不斷努力的動力​,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM