/// <summary> /// 构造函数。 /// </summary> /// <param name="line">所属线路。</param> /// <param name="from">来源站点。</param> /// <param name="to">目标站点。</param> /// <exception cref="ArgumentNullException">如果from或to为空引用,则抛出该异常。</exception> public MetroLink(MetroLine line, MetroNode from, MetroNode to) { if (line == null) throw new ArgumentNullException("line"); if (from == null) throw new ArgumentNullException("from"); if (to == null) throw new ArgumentNullException("to"); m_line = line; m_from = from; m_to = to; }
/// <summary> /// 获取地铁站点的矩形区域。 /// </summary> /// <param name="node">地铁站点。</param> /// <returns></returns> private Rectangle GetNodeRect(MetroNode node) { int r = node.Links.Count > 2 ? 7 : 5; return new Rectangle(node.X - r, node.Y - r, (r << 1) + 1, (r << 1) + 1); }
/// <summary> /// 绘制地铁站点。 /// </summary> /// <param name="g">绘图图面。</param> /// <param name="node">地铁站点。</param> private void PaintNode(Graphics g, MetroNode node) { //绘制站点圆圈 Color color = node.Links.Count > 2 ? Color.Black : node.Links[0].Line.Color; var rect = GetNodeRect(node); g.FillEllipse(Brushes.White, rect); using (Pen pen = new Pen(color)) { g.DrawEllipse(pen, rect); } //绘制站点名称 var sz = g.MeasureString(node.Name, this.Font).ToSize(); Point pt = new Point(node.X - sz.Width / 2, node.Y + (rect.Height >> 1) + 4); g.DrawString(node.Name, Font, Brushes.Black, pt); }
/// <summary> /// 查找指定两个节点之间的最短路径。 /// </summary> /// <param name="startNode">开始节点。</param> /// <param name="endNode">结束节点。</param> /// <param name="line">目标线路。</param> /// <returns>乘车路线列表。</returns> private List<MetroPath> FindShortestPaths(MetroNode startNode, MetroNode endNode, MetroLine line) { List<MetroPath> pathtList = new List<MetroPath>(); if (startNode == endNode) return pathtList; //路径队列,用于遍历路径 Queue<MetroPath> pathQueue = new Queue<MetroPath>(); pathQueue.Enqueue(new MetroPath()); while (pathQueue.Count > 0) { var path = pathQueue.Dequeue(); //如果已经超过最短路径,则直接返回 if (pathtList.Count > 0 && path.Links.Count > pathtList[0].Links.Count) continue; //路径的最后一个节点 MetroNode prevNode = path.Links.Count > 0 ? path.Links[path.Links.Count - 1].From : null; MetroNode lastNode = path.Links.Count > 0 ? path.Links[path.Links.Count - 1].To : startNode; //继续寻找后续节点 foreach (var link in lastNode.Links.Where(c => c.To != prevNode && (line == null || c.Line == line))) { if (link.To == endNode) { MetroPath newPath = path.Append(link); if (pathtList.Count == 0 || newPath.Links.Count == pathtList[0].Links.Count) {//找到一条路径 pathtList.Add(newPath); } else if (newPath.Links.Count < pathtList[0].Links.Count) {//找到一条更短的路径 pathtList.Clear(); pathtList.Add(newPath); } else break;//更长的路径没有意义 } else if (!path.ContainsNode(link.To)) { pathQueue.Enqueue(path.Append(link)); } } } return pathtList; }
/// <summary> /// 查找一次中转的路线。 /// </summary> /// <param name="startNode">开始节点。</param> /// <param name="endNode">结束节点。</param> /// <returns>乘车路线。</returns> private MetroPath FindOneTransferPath(MetroNode startNode, MetroNode endNode) { List<MetroPath> pathList = new List<MetroPath>(); foreach (var startLine in startNode.Links.Select(c => c.Line).Distinct()) { foreach (var endLine in endNode.Links.Select(c => c.Line).Where(c=> c != startLine).Distinct()) { //两条线路的中转站 foreach (var transferNode in this.Graph.GetTransferNodes(startLine, endLine)) { //起点到中转站的直达路线 var startDirectPathList = FindShortestPaths(startNode, transferNode, startLine); //中转站到终点的直达路线 var endDirectPathList = FindShortestPaths(transferNode, endNode, endLine); //合并两条直达路线 foreach (var startDirectPath in startDirectPathList) { foreach (var endDirectPath in endDirectPathList) { var directPath = startDirectPath.Merge(endDirectPath); pathList.Add(directPath); } } } } } //挑选最短路线 return GetShortestPath(pathList); }
/// <summary> /// 查找直达路线。 /// </summary> /// <param name="startNode">开始节点。</param> /// <param name="endNode">结束节点。</param> /// <returns>乘车路线。</returns> private MetroPath FindDirectPath(MetroNode startNode, MetroNode endNode) { MetroPath path = new MetroPath(); var startLines = startNode.Links.Select(c => c.Line).Distinct().ToList(); var endLines = endNode.Links.Select(c => c.Line).Distinct().ToList(); var lines = startLines.Where(c => endLines.Contains(c)).ToList(); if (lines.Count == 0) return path; //查找直达路线 List<MetroPath> pathList = new List<MetroPath>(); foreach (var line in lines) { pathList.AddRange(FindShortestPaths(startNode, endNode, line)); } //挑选最短路线 return GetShortestPath(pathList); }
/// <summary> /// 查找乘车路线。 /// </summary> /// <param name="startNode">起点。</param> /// <param name="endNode">终点。</param> /// <returns>乘车路线。</returns> public MetroPath FindPath(MetroNode startNode, MetroNode endNode) { MetroPath path = new MetroPath(); if (startNode == null || endNode == null) return path; if (startNode == endNode) return path; //如果起点和终点拥有共同线路,则查找直达路线 path = FindDirectPath(startNode, endNode); if (path.Links.Count > 0) return path; //如果起点和终点拥有一个共同的换乘站点,则查找一次换乘路线 path = FindOneTransferPath(startNode, endNode); if (path.Links.Count > 0) return path; //查找路径最短的乘车路线 var pathList = FindShortestPaths(startNode, endNode, null); //查找换乘次数最少的一条路线 int minTransfers = int.MaxValue; foreach (var item in pathList) { var curTransfers = item.Transfers; if (curTransfers < minTransfers) { minTransfers = curTransfers; path = item; } } return path; }
/// <summary> /// 判断是否包含指定的站点。 /// </summary> /// <param name="node">目标站点。</param> /// <returns></returns> /// <exception cref="ArgumentNullException">如果node为空引用,则跑出该异常。</exception> public bool ContainsNode(MetroNode node) { if (node == null) throw new ArgumentNullException("node"); return Links.FirstOrDefault(c => c.From == node || c.To == node) != null; }