导航路径的计算,一般用 Dijkstra 算法和 A* 算法。
Dijkstra 算法是全局遍历,确保运算结果一定是最短路径。
A* 算法是策略寻路,不保证一定是最短路径。
Dijkstra 需要载入全部数据,遍历搜索。(也可以分层计算,分层载入)
A* 算法可以根据需要,分部分块载入地图数据。 本代码是基于 A* 算法搜索最短路径的一个演示程序。
附件源码中的道路属性类参考了高德导航地图的数据结构。
- /// <summary>
- /// 搜索最短路径
- /// </summary>
- /// <param name="starts">起始节点描述序列</param>
- /// <param name="endPt">终止点坐标值</param>
- /// <param name="endsID">终止节点ID序列</param>
- /// <param name="avoidsID">避开节点ID序列</param>
- /// <returns></returns>
- public int[] CreateShortPath(ExtendLine startELine, ExtendLine endELine, int[] avoids)
- {
- ExtendLine sl = startELine;
- ExtendLine el = endELine;
- // 检测入口参数的合法性
- // ...
-
- // 检查起始节点内是否包含终止节点,如果包含,直接返回此点
- Debug.Assert( sl.IsOut == false && el.IsOut == true );
- if (( sl.FLine != null && el.FLine != null && el.FLine.TNodeID == el.FLine.FNodeID ) ||
- ( sl.FLine != null && el.TLine != null && sl.FLine.TNodeID == el.TLine.FNodeID ))
- return new int[] { sl.FLine.TNodeID };
-
- if (( sl.TLine != null && el.FLine != null && sl.TLine.TNodeID == el.FLine.FNodeID ) ||
- ( sl.TLine != null && el.TLine != null && sl.TLine.TNodeID == el.TLine.FNodeID ))
- return new int[] { sl.TLine.TNodeID };
-
- // 初始化搜索队列
- AStarQueue queue = this.InitSearchQueue( sl, el, avoids );
-
- // 搜索最短路径
- int pos = this.ShortSearch( queue, el.VLine.Points[1] );
-
- // 返回路径信息
- return this.TracePath( queue, pos );
- }
-
- /// <summary>
- /// 初始化搜索队列
- /// </summary>
- private AStarQueue InitSearchQueue(ExtendLine sl, ExtendLine el, int[] avoidsID)
- {
- // 创建队列
- AStarQueue queue = new AStarQueue( _gTab );
-
- // 起始点加入开放列表
- Debug.Assert( sl.FLine != null || sl.TLine != null );
- if ( sl.FLine != null )
- {
- Debug.Assert( sl.FLine.TNodeID > 0 );
- GraphNode gn = GraphNode.SearchByID( _gTab, sl.FLine.TNodeID );
- float h = GeoMath.Distance( gn.Point, el.VLine.Points[1] );
- queue.SetOpen( gn.NodePos, -1, sl.FLine.Length, h );
- }
- if ( sl.TLine != null )
- {
- Debug.Assert( sl.TLine.TNodeID > 0 );
- GraphNode gn = GraphNode.SearchByID( _gTab, sl.TLine.TNodeID );
- float h = GeoMath.Distance( gn.Point, el.VLine.Points[1] );
- queue.SetOpen( gn.NodePos, -1, sl.TLine.Length, h );
- }
-
- // 终止点加入终止列表
- if ( el.FLine != null )
- {
- Debug.Assert( el.FLine.FNodeID > 0 );
- GraphNode gn = GraphNode.SearchByID( _gTab, el.FLine.FNodeID );
- Debug.Assert( ! queue.IsOpen( gn.NodePos ) ); // 起始点不能与终止点重合
- queue.SetEnd( gn.NodePos );
- }
- if ( el.TLine != null )
- {
- Debug.Assert( el.TLine.FNodeID > 0 );
- GraphNode gn = GraphNode.SearchByID( _gTab, el.TLine.FNodeID );
- Debug.Assert( ! queue.IsOpen( gn.NodePos ) );
- queue.SetEnd( gn.NodePos );
- }
-
- // 避开点加入封闭列表
- if ( avoidsID != null )
- {
- foreach ( int nid in avoidsID )
- {
- GraphNode gn = GraphNode.SearchByID( _gTab, nid );
- if ( queue.IsOpen( gn.NodePos ) )
- continue;
- else if ( queue.IsEnd( gn.NodePos ) )
- continue;
- else
- queue.SetClose( gn.NodePos );
- }
- }
-
- // 返回队列
- return queue;
- }
-
- /// <summary>
- /// 在设置好的队列内搜索最短路径
- /// </summary>
- /// <param name="queue">已设置好起止节点的队列</param>
- /// <param name="endPoint">终止点坐标</param>
- /// <returns>终止节点的位置(Pos)</returns>
- private int ShortSearch(AStarQueue queue, FPoint endPt)
- {
- // 开始搜索
- while ( queue.Min >= 0 )
- {
- // 当前点加入封闭列表
- int curPos = queue.Min;
- queue.SetClose( curPos );
-
- GraphNode gnCur = this._gTab[curPos] as GraphNode;
- foreach ( GraphLink glnk in gnCur.Links )
- {
- // 获取连接点
- int nextPos = glnk.NodePos;
- GraphNode gnNext = this._gTab[nextPos] as GraphNode;
- float g = queue.GValue( curPos ) + glnk.Length;
- float h = GeoMath.Distance( gnNext.Point, endPt );
-
- if ( ! queue.IsEnd( gnNext.NodePos ) ) // 未达终点
- {
- // 不在封闭列表
- if ( ! queue.IsClose( nextPos ) )
- {
- // 开放列表内
- if ( queue.IsOpen( nextPos ) )
- {
- if ( g < queue.GValue( nextPos ) )
- {
- queue.SetOpen( nextPos, curPos, g, h );
- }
- }
- else
- {
- queue.SetOpen( nextPos, curPos, g, h );
- }
- }
- }
- else // 抵达终点
- {
- queue.SetOpen( nextPos, curPos, g, h );
- queue.SetClose( nextPos );
- return nextPos;
- }
- }
- }
- return -1; // 不可到达
- }
-
- /// <summary>
- /// 获取路径数据
- /// </summary>
- /// <param name="queue">已搜出路径的队列</param>
- /// <param name="pos">回溯操作的起始点</param>
- /// <returns>NodeID, RoadID, NodeID, RoadID, ... , NodeID</returns>
- private int[] TracePath(AStarQueue queue, int endPos)
- {
- // 获取路径
- ArrayList idls = new ArrayList();
- for ( int pos = endPos; pos >= 0; pos = queue[pos].ParentPos )
- {
- GraphNode gn = _gTab[pos] as GraphNode;
- idls.Add( gn.NodeID );
-
- int parentPos = queue[pos].ParentPos;
- if ( parentPos >= 0 )
- {
- GraphNode pn = _gTab[parentPos] as GraphNode;
- foreach ( GraphLink glnk in pn.Links )
- {
- if ( glnk.NodePos == pos )
- {
- idls.Add( glnk.RoadID );
- break;
- }
- }
- }
- }
-
- idls.Reverse();
- int[] ids = new int[idls.Count];
- idls.CopyTo( ids );
-
- Debug.Assert( this.ValidResult( ids ) );
- return ids;
- }
复制代码
|
|