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

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

站点智能提示:

项目结构图:

我的开发思路:

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

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

 /// <summary>
/// 初始化地铁线路数据
/// </summary>
/// <param name="city">城市</param>
public static void InitSubwayLine(CityEnum city)
{
if (AllSubwayLines != null && AllSubwayLines.Any() && _currentCity == city) return;
_currentCity = city;
var xmlName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "xml/" + city.ToString() + ".xml");
_doc = XDocument.Load(xmlName);
AllSubwayLines = _doc.Root.Elements().Select(x =>
{
var line = new SubwayLine
{
No = x.Attribute("No").Value,
Name = x.Attribute("Name").Value,
IsRound = x.Attribute("IsRound") == null ? false : bool.Parse(x.Attribute("IsRound").Value),
Stations = x.Elements().Select((y, i) => new Station
{
Index = i,
Name = y.Attribute("Name").Value,
LineNo = x.Attribute("No").Value,
CanTransfer = y.Attribute("CanTransfer") == null ? false : bool.Parse(y.Attribute("CanTransfer").Value),
TransferNo = y.Attribute("TransferNo") == null ? null : y.Attribute("TransferNo").Value
}).ToList()
};
var translines = line.GetTransStations().Select(z => z.TransferNo.Split(',')).ToList();
foreach (var transline in translines)
{
foreach (var li in transline)
{
line.TransferLines.Add(li);
}
}
line.TransferLines = line.TransferLines.Distinct().ToList();
return line;
}).ToList();
}

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

         /// <summary>
/// 获取地铁乘车信息
/// </summary>
/// <param name="depStation">出发站</param>
/// <param name="destStation">到达站</param>
/// <param name="city">所在城市</param>
/// <returns>返回各种换乘方案</returns>
public static List<QueryResult> GetRideSubwayInfo(string depStation, string destStation, CityEnum city)
{
InitSubwayLine(city);
if (string.IsNullOrWhiteSpace(depStation) || string.IsNullOrWhiteSpace(destStation)
|| !AllSubwayLines.Exists(x => x.Stations.Exists(y => y.Name.Equals(depStation)))
|| !AllSubwayLines.Exists(x => x.Stations.Exists(y => y.Name.Equals(destStation))))
return null;//出发站或到达站在线路上不存在! //各种换乘提示
//同一条线路
var msg_oneline = "在{0}【{1}】上车,经过{2}站到达目的站【{3}】。\r\n具体线路为:\r\n(出发){4}(到达)\r\n总搭乘站点数:{5}\r\n";
//换乘1次
var msg_transOnce = "在{0}【{1}】上车,经过{2}站在【{3}】下车,换乘{4},经过{5}站到达目的站【{6}】。\r\n具体线路为:\r\n(出发){7}(此处换乘{4})-->{8}(到达)\r\n总搭乘站点数:{9}\r\n";
//换乘2次
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";
//换乘3次
var msg_transThreetimes = "在{0}【{1}】上车,经过{2}站在【{3}】下车,换乘{4},经过{5}站在【{6}】下车,换乘{7},经过{8}站在【{9}】下车,换乘{10},经过{11}站到达目的站【{12}】。"
+ "\r\n具体线路为:\r\n(出发){13}(此处换乘{4})-->{14}(此处换乘{7})-->{15}(此处换乘{10})-->{16}(到达)\r\n总搭乘站点数:{17}\r\n";
//换乘4次
var msg_transFourtimes = "在{0}【{1}】上车,经过{2}站在【{3}】下车,换乘{4},经过{5}站在【{6}】下车,换乘{7},经过{8}站在【{9}】下车,换乘{10},经过{11}站在【{12}】下车,换乘{13},经过{14}站到达目的站【{15}】。"
+ "\r\n具体线路为:\r\n(出发){16}(此处换乘{4})-->{17}(此处换乘{7})-->{18}(此处换乘{10})-->{19}(此处换乘{13})-->{20}(到达)\r\n总搭乘站点数:{21}\r\n"; //保存各种换乘方案
var result = new List<QueryResult>();
//第一步:先查找始发站和到达站在哪一条线路
var afterDepLines = GetAcrossLines(depStation);
var afterDestLines = GetAcrossLines(destStation);
//根据同一条线和不同线路展开分析
if (IsSameLine(depStation, destStation))
{
#region 同一条线路
var commLines = afterDepLines.Where(x => afterDestLines.Select(y => y.No).Contains(x.No)).ToList();
//判断线路是否相同,相同直接计算站点距离
var depIndex = GetIndexOnLine(depStation, commLines.First());
var destIndex = GetIndexOnLine(destStation, commLines.First());
var crossStations = commLines.First().Stations.Between(depIndex, destIndex).Select(x => x.Name).ToList();
var range = crossStations.Count - ;
if (depIndex > destIndex) crossStations.Reverse();
var rs = msg_oneline.FormatTo(commLines.First().ToString(), depStation, range, destStation,
crossStations.ToJoinString(), range);
result.Add(new QueryResult() { Description = rs, Range = range });
#endregion
}
else
{
#region 不同线路
if (!IsTransferStation(depStation) && !IsTransferStation(destStation))//如果始发站和终点站都不是换乘站,则表示始发站和到达站都是只有一条线路通过
{
if (afterDepLines.First().IsIntersect(afterDestLines.First()))
{
#region 如果两条线路交叉,一定有换乘站点
var clist = GetAcrossStations(afterDepLines.First(), afterDestLines.First()).Select(x => x.Name).ToList();
var i = GetIndexOnLine(depStation, afterDepLines.First());
var j = GetIndexOnLine(clist.First(), afterDepLines.First());
var k = GetIndexOnLine(destStation, afterDestLines.First());
var l = GetIndexOnLine(clist.First(), afterDestLines.First());
var coss1 = afterDepLines.First().Stations.Between(i, j).Select(x => x.Name).ToList();
var coss2 = afterDestLines.First().Stations.Between(k, l).Select(x => x.Name).ToList();
if (i > j) coss1.Reverse();
if (k < l) coss2.Reverse();
var rang1 = coss1.Count - ;
var rang2 = coss2.Count - ;
var h = rang1 + rang2; //站点数
var rs = msg_transOnce.FormatTo(afterDepLines.First().ToString(), depStation, rang1, clist.First(),
afterDestLines.First().ToString(), rang2, destStation,
coss1.ToJoinString(), coss2.Where(x => x != clist.First()).ToJoinString(), h);
result.Add(new QueryResult()
{
Description = rs,
Range = h,
TransferStations = new List<string>() { clist.First() },
TransferTimes =
});
#endregion
}
else
{
#region 不交叉,需要通过第三条线路换乘,即多次换乘
var depSta = GetStation(depStation);
var destSta = GetStation(destStation);
//找出两条线路的可换乘站点,找出可换乘相同线路的站点
var trans1 = afterDepLines.First().GetTransStations();
var trans2 = afterDestLines.First().GetTransStations();
var trans3 = new List<Station>();
var trans4 = new List<Station>();
var expets = trans1.Join(trans2, x => x.TransferNo, y => y.TransferNo, (x, y) =>
{
trans3.Add(x);
trans4.Add(y);
return x.Name + "---" + y.Name;
}).ToList();
if (expets.Any())
{
#region 两次换乘
//trans3.Count和trans4.Count必定相等
//计算最短距离,列出所有换乘方案
for (var i = ; i < trans3.Count; i++)
{
var tranLine = GetLine(trans3[i].TransferNo);
//获取这两个站点在此线路的索引
var ix1 = depSta.Index;
var ix2 = destSta.Index;
var iix1 = GetIndexOnLine(trans3[i].Name, depSta.LineNo);
var iix2 = GetIndexOnLine(trans4[i].Name, destSta.LineNo);
var tx1 = GetIndexOnLine(trans3[i].Name, tranLine);
var tx2 = GetIndexOnLine(trans4[i].Name, tranLine); var depRange = afterDepLines.First().Stations.Between(ix1, iix1).Select(x => x.Name).ToList();
var destRange = afterDestLines.First().Stations.Between(ix2, iix2).Select(x => x.Name).ToList();
var transRange = tranLine.Stations.Between(tx1, tx2).Select(x => x.Name).ToList();
if (ix1 > iix1) depRange.Reverse();
if (ix2 < iix2) destRange.Reverse();
if (tx1 > tx2) transRange.Reverse();
var r1 = depRange.Count - ;
var r2 = destRange.Count - ;
var r3 = transRange.Count - ;
var r = r1 + r2 + r3;
var rs = msg_transTwice.FormatTo(afterDepLines.First().ToString(), depStation, r1,
trans3[i].Name,
tranLine.ToString(), r3, trans4[i].Name, afterDestLines.First().ToString(), r2,
destStation, depRange.ToJoinString(),
transRange.Where(x => !x.IsSame(trans3[i].Name) && !x.IsSame(trans4[i].Name)).ToJoinString(),
destRange.ToJoinString(), r);
result.Add(new QueryResult()
{
Description = rs,
Range = r,
TransferTimes = ,
TransferStations = new List<string>() { trans3[i].Name, trans4[i].Name }
});
}
#endregion
}
#region 查找3次以上换乘的可能结果,寻求最短距离
var trlines1 = afterDepLines.First().TransferLines.Select(GetLine).ToList();
var trlines2 = afterDestLines.First().TransferLines.Select(GetLine).ToList();
var destss = new List<Station>(); #region 换乘3次
foreach (var depline in trlines1)
{
foreach (var destline in trlines2)
{
var ss = destline.GetAcrossStations(depline);
if (!ss.Any()) continue; //3次换乘
var slist1 = afterDepLines.First().GetAcrossStations(depline);
if (!slist1.Any()) continue;
var s1 = slist1.GetClosestStation(depSta.Name);
var s1_ix1 = depSta.Index;
var s1_ix2 = s1.Index;
var s1_range =
afterDepLines.First()
.Stations.Between(s1_ix1, s1_ix2)
.Select(x => x.Name)
.ToList();
var s1_h = s1_range.Count - ;
if (s1_ix1 > s1_ix2) s1_range.Reverse(); var s2_ix1 = GetIndexOnLine(s1.Name, depline);
var s2_ix2 = GetIndexOnLine(ss.First().Name, depline);
var s2_range = depline.Stations.Between(s2_ix1, s2_ix2).Select(x => x.Name).ToList();
var s2_h = s2_range.Count - ;
if (s2_ix1 > s2_ix2) s2_range.Reverse(); var slist3 = destline.GetAcrossStations(afterDestLines.First());
if (!slist3.Any()) continue;
var s3 = slist3.GetClosestStation(ss.First().Name);
var s3_ix1 = s3.Index;
var s3_ix2 = ss.First().Index;
var s3_range = destline.Stations.Between(s3_ix1, s3_ix2).Select(x => x.Name).ToList();
var s3_h = s3_range.Count - ;
if (s3_ix1 < s3_ix2) s3_range.Reverse(); var s4_ix1 = GetIndexOnLine(s3.Name, afterDestLines.First());
var s4_ix2 = destSta.Index;
var s4_range =
afterDestLines.First()
.Stations.Between(s4_ix1, s4_ix2)
.Select(x => x.Name)
.ToList();
var s4_h = s4_range.Count - ;
if (s4_ix1 > s4_ix2) s4_range.Reverse(); var h = s1_h + s2_h + s3_h + s4_h;
var rs = msg_transThreetimes.FormatTo(afterDepLines.First().ToString(), depStation,
s1_h, s1.Name,
depline.ToString(), s2_h, ss.First().Name,
GetLine(ss.First().LineNo).ToString(), s3_h, s3.Name,
afterDestLines.First().ToString(), s4_h, destStation, s1_range.ToJoinString(),
s2_range.Where(x => x != s1.Name).ToJoinString(),
s3_range.Where(x => x != ss.First().Name).ToJoinString(),
s4_range.Where(x => x != s3.Name).ToJoinString(), h);
result.Add(new QueryResult()
{
Description = rs,
Range = h,
TransferTimes = ,
TransferStations =
new List<string>()
{
s1.Name,
ss.First().Name,
s3.Name
}
});
destss.AddRange(ss);
}
}
#endregion if (!destss.Any()) //换乘4次
{
#region 换乘4次
foreach (var depline in trlines1)
{
foreach (var destline in trlines2)
{
var deptrlines =
depline.TransferLines.Where(x => x != afterDepLines.First().No)
.Select(GetLine)
.ToList();
foreach (var line in deptrlines)
{
var s1 = line.GetAcrossStations(destline);
if (!s1.Any()) continue; //4次换乘
var trlist1 = afterDepLines.First().GetAcrossStations(depline);
if (!trlist1.Any()) continue;
var tr1 = trlist1.GetClosestStation(depSta.Name);
var s1_ix1 = depSta.Index;
var s1_ix2 = tr1.Index;
var s1_range =
afterDepLines.First()
.Stations.Between(s1_ix1, s1_ix2)
.Select(x => x.Name)
.ToList();
var h1 = s1_range.Count - ;
if (s1_ix1 > s1_ix2) s1_range.Reverse(); var trlist2 = GetLine(tr1.TransferNo).GetAcrossStations(line);
if (!trlist2.Any()) continue;
var tr2 = trlist2.GetClosestStation(tr1.Name);
var s2_ix1 = GetIndexOnLine(tr1.Name, depline);
var s2_ix2 = tr2.Index;
var s2_range =
depline.Stations.Between(s2_ix1, s2_ix2)
.Select(x => x.Name)
.ToList();
var h2 = s2_range.Count - ;
if (s2_ix1 > s2_ix2) s2_range.Reverse(); var s3_ix1 = GetIndexOnLine(tr2.Name, line);
var s3_ix2 = s1.First().Index;
var s3_range =
line.Stations.Between(s3_ix1, s3_ix2)
.Select(x => x.Name)
.ToList();
var h3 = s3_range.Count - ;
if (s3_ix1 > s3_ix2) s3_range.Reverse(); var trlist3 = destline.GetAcrossStations(afterDestLines.First());
if (!trlist3.Any()) continue;
var tr3 = trlist3.GetClosestStation(s1.First().Name);
var s4_ix1 = GetIndexOnLine(s1.First().Name, destline);
var s4_ix2 = tr3.Index;
var s4_range =
destline.Stations.Between(s4_ix1, s4_ix2)
.Select(x => x.Name)
.ToList();
var h4 = s4_range.Count - ;
if (s4_ix1 > s4_ix2) s4_range.Reverse(); var s5_ix1 = GetIndexOnLine(tr3.Name, afterDestLines.First());
var s5_ix2 = destSta.Index;
var s5_range =
afterDestLines.First()
.Stations.Between(s5_ix1, s5_ix2)
.Select(x => x.Name)
.ToList();
var h5 = s5_range.Count - ;
if (s5_ix1 > s5_ix2) s5_range.Reverse();
var h = h1 + h2 + h3 + h4 + h5;
var rs =
msg_transFourtimes.FormatTo(afterDepLines.First().ToString(),
depStation, h1, tr1.Name,
depline.ToString(), h2, tr2.Name,
line.ToString(), h3, s1.First().Name,
destline.ToString(), h4, tr3.Name,
afterDestLines.First().ToString(), h5, destStation,
s1_range.ToJoinString(),
s2_range.Where(x => x != tr1.Name).ToJoinString(),
s3_range.Where(x => x != tr2.Name).ToJoinString(),
s4_range.Where(x => x != tr2.Name && x != s1.First().Name).ToJoinString(),
s5_range.Where(x => x != tr3.Name).ToJoinString(), h);
result.Add(new QueryResult()
{
Description = rs,
Range = h,
TransferTimes = ,
TransferStations =
new List<string>()
{
tr1.Name,
tr2.Name,
s1.First().Name,
tr3.Name
}
});
destss.AddRange(s1);
}
}
}
#endregion
}
if (!destss.Any())//换乘4次以上
{ }
#endregion
#endregion
}
}
else //始发站和到达站有其中一个是换乘站
{
//找出到达站经过的路线和始发站所在路线的交叉线路
var crossLines = GetAcrossLines(depStation, destStation);
//分三种情况:1、始发站不是换乘站而到达站是换乘站;2、始发站是换乘站而到达站不是换乘站;3、始发站和到达站都是换乘站,根据情况展开分析
if (!IsTransferStation(depStation) && IsTransferStation(destStation))
{
#region 情况1:始发站不是换乘站而到达站是换乘站
if (crossLines.Count > ) //依赖交叉线
{
var listTrans = new List<Station>();
foreach (var line in crossLines)
{
//找出每条交叉线换乘到始发站线路的所有换乘站
var ss = line.GetTransStations()
.Where(x => x.TransferNo.IsSame(afterDepLines.First().No))
.ToList();
listTrans.AddRange(ss);
}
var depIx = GetIndexOnLine(depStation, afterDepLines.First());
var tranStas =
listTrans.Select(
s => new { sta = s, ix = GetIndexOnLine(s.Name, afterDepLines.First()) })
.ToList();
foreach (var sta in tranStas)
{
var destIx = GetIndexOnLine(destStation, sta.sta.LineNo);
var tranIx = GetIndexOnLine(sta.sta.Name, sta.sta.LineNo);
var coss1 =
afterDepLines.First()
.Stations.Between(depIx, sta.ix)
.Select(x => x.Name)
.ToList();
var coss2 =
GetLine(sta.sta.LineNo)
.Stations.Between(destIx, tranIx)
.Select(x => x.Name)
.ToList();
var rang1 = coss1.Count - ;
var rang2 = coss2.Count - ;
var h = rang1 + rang2; //站点数
if (depIx > sta.ix) coss1.Reverse();
if (destIx < tranIx) coss2.Reverse(); var rs = msg_transOnce.FormatTo(afterDepLines.First().ToString(), depStation, rang1,
sta.sta.Name,
GetLine(sta.sta.LineNo).ToString(), rang2, destStation,
coss1.ToJoinString(), coss2.Where(x => x != sta.sta.Name).ToJoinString(), h);
result.Add(new QueryResult()
{
Description = rs,
Range = h,
TransferTimes = ,
TransferStations = new List<string>() { sta.sta.Name }
});
}
}
//查找其他换乘可能
var depSta = GetStation(depStation);
var trlinesDep = afterDepLines.First().TransferLines.Select(GetLine).ToList();
foreach (var depline in afterDestLines)
{
var trlineItems = depline.TransferLines.Select(GetLine).ToList();
foreach (var iline in trlineItems)
{
foreach (var destline in trlinesDep)
{
var ss = destline.GetAcrossStations(iline);
if (!ss.Any()) continue; //3次换乘
var slist1 = afterDepLines.First().GetAcrossStations(destline);
if (!slist1.Any()) continue;
var s1 = slist1.GetClosestStation(depStation);
var s1_ix1 = depSta.Index;
var s1_ix2 = s1.Index; var s1_range =
afterDepLines.First()
.Stations.Between(s1_ix1, s1_ix2)
.Select(x => x.Name)
.ToList();
var s1_h = s1_range.Count - ;
if (s1_ix1 > s1_ix2) s1_range.Reverse(); var s2 = ss.GetClosestStation(s1.Name);
var s2_ix1 = GetIndexOnLine(s1.Name, destline);
var s2_ix2 = GetIndexOnLine(s2.Name, destline);
var s2_range = destline.Stations.Between(s2_ix1, s2_ix2).Select(x => x.Name).ToList();
var s2_h = s2_range.Count - ;
if (s2_ix1 > s2_ix2) s2_range.Reverse(); var slist3 = iline.GetAcrossStations(depline);
if (!slist3.Any()) continue;
var s3 = slist3.GetClosestStation(s2.Name);
var s3_ix1 = s3.Index;
var s3_ix2 = GetIndexOnLine(s2.Name, iline);
var s3_range = iline.Stations.Between(s3_ix1, s3_ix2).Select(x => x.Name).ToList();
var s3_h = s3_range.Count - ;
if (s3_ix1 < s3_ix2) s3_range.Reverse(); var s4_ix1 = GetIndexOnLine(s3.Name, depline);
var s4_ix2 = GetIndexOnLine(destStation, depline);
var s4_range = depline.Stations.Between(s4_ix1, s4_ix2)
.Select(x => x.Name)
.ToList();
var s4_h = s4_range.Count - ;
if (s4_ix1 > s4_ix2) s4_range.Reverse(); var h = s1_h + s2_h + s3_h + s4_h;
if (s2_h == || s3_h == || s4_h == || destline.No.IsSame(iline.No)) continue;
var rs = msg_transThreetimes.FormatTo(afterDepLines.First().ToString(),
depStation,
s1_h, s1.Name,
destline.ToString(), s2_h, s2.Name,
iline.ToString(), s3_h, s3.Name,
depline.ToString(), s4_h, destStation, s1_range.ToJoinString(),
s2_range.Where(x => x != s1.Name).ToJoinString(),
s3_range.Where(x => x != s2.Name).ToJoinString(),
s4_range.Where(x => x != s3.Name).ToJoinString(), h);
result.Add(new QueryResult()
{
Description = rs,
Range = h,
TransferTimes = ,
TransferStations =
new List<string>()
{
s1.Name,
s2.Name,
s3.Name
}
});
}
}
}
#endregion
}
if (IsTransferStation(depStation) && !IsTransferStation(destStation))
{
#region 情况2:始发站是换乘站而到达站不是换乘站
var transLines =
afterDepLines.Where(
x =>
x.Stations.Exists(
y => y.CanTransfer && y.TransferNo.Contains(afterDestLines.First().No)))
.ToList();
if (transLines.Any())
{
var clist =
GetAcrossStations(transLines.First(), afterDestLines.First())
.Select(x => x.Name)
.ToList();
var i = GetIndexOnLine(depStation, transLines.First());
var j = GetIndexOnLine(clist.First(), transLines.First());
var k = GetIndexOnLine(destStation, afterDestLines.First());
var l = GetIndexOnLine(clist.First(), afterDestLines.First());
var coss1 = transLines.First().Stations.Between(i, j).Select(x => x.Name).ToList();
var coss2 = afterDestLines.First().Stations.Between(k, l).Select(x => x.Name).ToList();
var rang1 = coss1.Count - ;
var rang2 = coss2.Count - ;
var h = rang1 + rang2; //站点数
if (i > j) coss1.Reverse();
if (k < l) coss2.Reverse(); var rs = msg_transOnce.FormatTo(transLines.First().ToString(), depStation, rang1,
clist.First(),
afterDestLines.First().ToString(), rang2, destStation,
coss1.ToJoinString(), coss2.Where(x => x != clist.First()).ToJoinString(), h);
result.Add(new QueryResult()
{
Description = rs,
Range = h,
TransferTimes = ,
TransferStations = new List<string>() { clist.First() }
});
}
//寻找其他换乘可能
var destSta = GetStation(destStation);
var trlinesDest = afterDestLines.First().TransferLines.Select(GetLine).ToList();
foreach (var depline in afterDepLines)
{
var trlineItems = depline.TransferLines.Select(GetLine).ToList();
foreach (var iline in trlineItems)
{
foreach (var destline in trlinesDest)
{
var ss = iline.GetAcrossStations(destline);
if (!ss.Any()) continue; //3次换乘
var slist1 = depline.GetAcrossStations(iline);
if (!slist1.Any()) continue;
var s1 = slist1.GetClosestStation(depStation);
var s1_ix1 = GetIndexOnLine(depStation, depline);
var s1_ix2 = s1.Index;
var s1_range =
depline.Stations.Between(s1_ix1, s1_ix2)
.Select(x => x.Name)
.ToList();
var s1_h = s1_range.Count - ;
if (s1_ix1 > s1_ix2) s1_range.Reverse(); var s2 = ss.GetClosestStation(s1.Name);
var s2_ix1 = GetIndexOnLine(s1.Name, iline);
var s2_ix2 = GetIndexOnLine(s2.Name, iline);
var s2_range = iline.Stations.Between(s2_ix1, s2_ix2).Select(x => x.Name).ToList();
var s2_h = s2_range.Count - ;
if (s2_ix1 > s2_ix2) s2_range.Reverse(); var slist3 = destline.GetAcrossStations(afterDestLines.First());
if (!slist3.Any()) continue;
var s3 = slist3.GetClosestStation(ss.First().Name);
var s3_ix1 = s3.Index;
var s3_ix2 = GetIndexOnLine(s2.Name, destline);
var s3_range = destline.Stations.Between(s3_ix1, s3_ix2).Select(x => x.Name).ToList();
var s3_h = s3_range.Count - ;
if (s3_ix1 < s3_ix2) s3_range.Reverse(); var s4_ix1 = GetIndexOnLine(s3.Name, afterDestLines.First());
var s4_ix2 = destSta.Index;
var s4_range =
afterDestLines.First()
.Stations.Between(s4_ix1, s4_ix2)
.Select(x => x.Name)
.ToList();
var s4_h = s4_range.Count - ;
if (s4_ix1 > s4_ix2) s4_range.Reverse(); var h = s1_h + s2_h + s3_h + s4_h;
if (s1_h == || s2_h == || s3_h == || iline.No.IsSame(destline.No)) continue;
var rs = msg_transThreetimes.FormatTo(depline.ToString(), depStation,
s1_h, s1.Name,
iline.ToString(), s2_h, s2.Name,
destline.ToString(), s3_h, s3.Name,
afterDestLines.First().ToString(), s4_h, destStation,
s1_range.ToJoinString(),
s2_range.Where(x => x != s1.Name).ToJoinString(),
s3_range.Where(x => x != s2.Name).ToJoinString(),
s4_range.Where(x => x != s3.Name).ToJoinString(), h);
result.Add(new QueryResult()
{
Description = rs,
Range = h,
TransferTimes = ,
TransferStations =
new List<string>()
{
s1.Name,
s2.Name,
s3.Name
}
});
}
}
}
#endregion
}
if (IsTransferStation(depStation) && IsTransferStation(destStation))
{
#region 情况3:始发站和到达站都是换乘站
if (crossLines.Count > ) //依赖交叉线
{
var transStations = GetClosestStation(depStation, true);
if (
!transStations.Exists(
x =>
crossLines.Exists(y => y.Stations.Exists(z => x.TransferNo.Split(',').Intersect(z.LineNo.Split(',')).Any()))))
{
transStations = new List<Station>();
afterDepLines.ForEach(x =>
{
var ctrans = x.GetTransStations();
var ctrans2 =
ctrans.Where(
y =>
crossLines.Exists(
z => z.Stations.Exists(zz => y.TransferNo.Split(',').Intersect(zz.LineNo.Split(',')).Any())))
.ToList();
transStations.AddRange(ctrans2);
});
}
var transLine =
afterDestLines.Where(
x =>
x.Stations.Exists(y => transStations.Exists(z => z.Name.IsSame(y.Name))))
.ToList();
var intersStas =
transStations.Where(
x =>
transLine.Exists(
y =>
y.Stations.Exists(
z => z.Name.IsSame(x.Name) && x.TransferNo.Split(',').Intersect(z.LineNo.Split(',')).Any())))
.ToList();
foreach (var line in transLine)
{
//分别获取换乘站在换乘线上的索引
foreach (var t in intersStas)
{
var ix = GetIndexOnLine(destStation, line); //目的站在换乘线上的索引
var iix = GetIndexOnLine(t.Name, line); //换乘站在换乘线上的索引
if (iix == -) continue;
var ix2 = GetIndexOnLine(depStation, t.LineNo); //始发站在换乘站所在线路上的索引
var iix2 = GetIndexOnLine(t.Name, t.LineNo); //换乘站在始发站所在线路上的索引 var ixRange = line.Stations.Between(ix, iix).Select(x => x.Name).ToList();
var ixRange2 = GetLine(t.LineNo).Stations.Between(ix2, iix2).Select(x => x.Name).ToList();
var ixh = ixRange.Count - ;
var ixh2 = ixRange2.Count - ;
var h = ixh + ixh2;
if (ix < iix) ixRange.Reverse();
if (ix2 > iix2) ixRange2.Reverse();
var rs = msg_transOnce.FormatTo(GetLine(t.LineNo).ToString(), depStation, ixh2, t.Name,
line.ToString(), ixh, destStation,
ixRange2.ToJoinString(),
ixRange.Where(x => !x.IsSame(t.Name)).ToJoinString(), h);
result.Add(new QueryResult()
{
Description = rs,
Range = h,
TransferTimes = ,
TransferStations = new List<string>() { t.Name }
});
}
}
}
#endregion
}
}
#endregion
} //查找其他可能性
#region 深度挖掘其他方案
//找出经过始发站和到达站的线路中共同穿过的公共线路,寻找换乘方案
var connLines = GetCommonLines(depStation, destStation);
if (connLines.Count > )
{
var transDep = new List<Station>();
var transDest = new List<Station>();
foreach (var depLine in afterDepLines)
{
//在经过始发站的线路中的站点中找到可换乘到公共线路的站点
var trans = depLine.GetTransStations()
.Where(x => x.Name != depStation && x.Name != destStation && connLines.Exists(y => x.TransferNo.Contains(y.No)))
.ToList();
transDep.AddRange(trans);
}
foreach (var destLine in afterDestLines)
{
//在经过到达站的线路中的站点中找到可换乘到公共线路的站点
var trans = destLine.GetTransStations()
.Where(x => x.Name != depStation && x.Name != destStation && connLines.Exists(y => x.TransferNo.Contains(y.No)))
.ToList();
transDest.AddRange(trans);
}
foreach (var d1 in transDep)
{
foreach (var d2 in transDest)
{
//找出交叉站点,即可换乘同一条公共线路的站点
var inters = d1.TransferNo.Split(',').Intersect(d2.TransferNo.Split(',')).ToList();
if (!inters.Any() || d1.Name.IsSame(d2.Name)) continue;
var tranLine = GetLine(inters.First());
var depLine = GetLine(d1.LineNo);
var destLine = GetLine(d2.LineNo);
var ix1 = GetIndexOnLine(depStation, depLine);
var ix2 = GetIndexOnLine(d1.Name, depLine);
var iix1 = GetIndexOnLine(d1.Name, tranLine);
var iix2 = GetIndexOnLine(d2.Name, tranLine);
var iiix1 = GetIndexOnLine(d2.Name, destLine);
var iiix2 = GetIndexOnLine(destStation, destLine); var depRange = depLine.Stations.Between(ix1, ix2).Select(x => x.Name).ToList();
var transRange = tranLine.Stations.Between(iix1, iix2).Select(x => x.Name).ToList();
var destRange = destLine.Stations.Between(iiix1, iiix2).Select(x => x.Name).ToList();
var r1 = depRange.Count - ;
var r2 = transRange.Count - ;
var r3 = destRange.Count - ;
var r = r1 + r2 + r3;
if (ix1 > ix2) depRange.Reverse();
if (iix1 > iix2) transRange.Reverse();
if (iiix1 > iiix2) destRange.Reverse();
string rs;
if (r1 > && r2 > && r3 > )
{
rs = msg_transTwice.FormatTo(depLine.ToString(), depStation, r1, d1.Name, tranLine.ToString(), r2,
d2.Name, destLine.ToString(), r3, destStation, depRange.ToJoinString(),
transRange.Where(x => !x.IsSame(d1.Name)).ToJoinString(),
destRange.Where(x => !x.IsSame(d1.Name) && !x.IsSame(d2.Name)).ToJoinString(), r);
result.Add(new QueryResult()
{
Description = rs,
Range = r,
TransferTimes = ,
TransferStations = new List<string>() { d1.Name, d2.Name }
});
}
else if (r1 > && r2 == && r3 > )
{
rs = msg_transOnce.FormatTo(GetLine(inters.First()).ToString(), depStation, r1, d2.Name,
destLine.ToString(), r3, destStation,
depRange.ToJoinString(),
destRange.Where(x => !x.IsSame(d2.Name)).ToJoinString(), r);
result.Add(new QueryResult()
{
Description = rs,
Range = r,
TransferTimes = ,
TransferStations = new List<string>() { d2.Name }
});
}
else
{
rs = msg_transOnce.FormatTo(depLine.ToString(), depStation, r1, d1.Name, tranLine.ToString(), r2,
destStation, depRange.ToJoinString(),
transRange.Where(x => !x.IsSame(d1.Name)).ToJoinString(), r);
result.Add(new QueryResult()
{
Description = rs,
Range = r,
TransferTimes = ,
TransferStations = new List<string>() { d1.Name }
});
}
}
}
}
#endregion //重新组织数据
result.ForEach(x =>
{
var desc = x.Description.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
x.Suggestion = desc[];
x.Route = desc[];
});
//去除重复方案,保留一个
var gyResult = result.GroupBy(x => x.Route).Select(x => x.First()).ToList();
//移除逗逼方案,例如:A-B-A-C
gyResult.RemoveAll(x => x.Route.Split(new string[] { depStation }, StringSplitOptions.RemoveEmptyEntries).Length >
|| x.Route.Split(new string[] { destStation }, StringSplitOptions.RemoveEmptyEntries).Length > );
return gyResult;
}

因为只做了北京、上海、深圳、广州四个城市的站点数据,所以演示只能查询这四个城市的地铁换乘,大家可以补充自己想要的城市的地铁数据,格式请参照源码中上面四个城市的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. Python模拟登录成功与失败处理方式(不涉及前端)

    任务说明: (1) 用户输入用户名,如不存在此用户不能登录: (2) 用户在输入密码时,如果连续输入三次错误,则该用户被锁定一段时间; (3) 用户被锁定一段时间后,可再次进行尝试登录: 程序使用库: ...

  2. Python-ORM之sqlalchemy的简单使用

    ORM之sqlalchemy 基础章节 使用SQLAlchemy链接数据库 from sqlalchemy import create_engine from sqlalchemy.ext.decla ...

  3. Windows驱动开发工具 WDK 学习笔记(1)

    目标:能够把电脑当作一个集成有高性能处理器的开发板用起来,当然,还自带了一个高级的操作系统Windows(必须的).总之,就是在一个带了操作系统的高性能开发板上的驱动程序开发. 性质:纯属业余爱好 1 ...

  4. directdraw显示yuv420(YV12)

    height=width=widthBytes=0;  m_screen.SetWindowPos(&CWnd::wndBottom,0,0,720,576, SWP_NOMOVE | SWP ...

  5. Caused by: org.xml.sax.SAXParseException; lineNumber: 28; columnNumber: 81;

    1.错误描述 严重: Exception sending context initialized event to listener instance of class org.springframe ...

  6. ubuntu14.04 64位 安装JDK1.7

    ubuntu14.04 64位 安装JDK1.7 1 新建文件夹 youhaidong@youhaidong:~$ sudo mkdir /usr/lib/jvm 2 解压文件 youhaidong@ ...

  7. 一篇关于PHP性能的文章

    一篇关于PHP性能的文章 昨晚清理浏览器收藏夹网址时,发现了http://www.phpbench.com/,想起来应该是2015年发现的一个比较性能的文章,我就点进去看了看,发现还是全英文耶,刚好最 ...

  8. 一次断电后docker问题的排解

    这篇文章是一次真实的排查经历,可能对大多数人没什么作用,慎看. 断电后来电,服务器启动,手工启动各个docker,大部分正常,小部分还是出错,错误信息: <html> <head&g ...

  9. 期望$DP$ 方法总结

    期望\(DP\) 方法总结 这个题目太大了,变化也层出不穷,这里只是我的一点心得,不定期更新! 1. 递推式问题 对于无穷进行的操作期望步数问题,一般可用递推式解决. 对于一个问题\(ans[x]\) ...

  10. [BZOJ1058][ZJOJ2007]报表统计

    BZOJ Luogu 题目描述 Q的妈妈是一个出纳,经常需要做一些统计报表的工作.今天是妈妈的生日,小Q希望可以帮妈妈分担一些工作,作为她的生日礼物之一. 经过仔细观察,小Q发现统计一张报表实际上是维 ...