如何实现复杂的导航功能
本文要求读者具备如下知识和技术背景:
1 熟悉Java开发,知道如何正确的编译运行Java代码;
2 知道GIS的基本概念,知道地图导航的基本概念;
3 对GeoTools有一定的认识。
一开始先来明确我们的任务:在基本的路径查询基础上
1 实现单行道限制
2 实现左右拐弯限制
3 实现动态路况限制
4 选择最短距离和最短时间
图和图的搜索
要想了解路径查询的算法,首先得了解一下它的数学模型“图”。简而言之,图就是一系列的点和点之间的连接关系。【Graph】
所谓路径查询,可以简化成对一个图的搜索。例如:从点6到点1的最短路径是,6-4-5-1,或者6-4-3-2-1,又或者6-4-5-2-3-4-5-1。如你所见,从6到1之间可以有许多条路径,其中第一条和第二条我们认为是合理的,第三条是不合理的,因为它有重复路段。所以查询节点间路径的算法就显得至关重要了。经典的查询算法有:Dijkstra和它的改进版本A*。这两个算法的实现各种语言都有,也很成熟了,我们不需要自己去写,但是最为一种训练,有兴趣的读者可以自己试着实现。
我在这里只介绍一下基本概念。从前面的例子我们看到两点间可以有多条路径,但是实际上我们一般只关心一条路径,就拿开车来说,我们关心驾驶时间最少的路。一般情况下,实际距离短驾驶时间就少,所以我们先从实际距离入手。作为搜索算法如何确定距离最短呢。6-4-5-1一定比6-4-3-2-1距离短吗。显然我们不能仅仅根据节点的个数来判断距离。事实是我们除了拥有点和点之间的连接外,还需要连接的属性,这里就是距离。用上面的例子我们来指定距离:
6-4距离10
4-5距离7
5-1距离6
4-3距离2
3-2距离4
2-1距离1
6-4-5-1距离10+7+6=23
6-4-3-2-1距离10+2+4+1=17
显然第二条路虽然节点多但是距离短。这里需要引人一个概念“开销”,在这个例子中目前我们使用路径距离来代表开销。我们总是选择开销小的路径。现实中影响驾驶时间的因素除了距离还有道路拥堵情况,所以我们增加属性,叫拥堵系数,假设距离乘上系数才是我们需要的开销:
6-4距离10 拥堵1
4-5距离7 拥堵2
5-1距离6 拥堵1
4-3距离2 拥堵13
3-2距离4 拥堵11
2-1距离1 拥堵11
6-4-5-1距离10*1+7*2+6*1=30
6-4-3-2-1距离10*1+2*13+4*11+1*11=91
可以看到,虽然第二条路的实际距离短,但是由于拥堵情况严重,它的开销远大于第一条路。在这种情况下我们应该选择实际距离长的第一条路。我们可以不断对上面的计算进行完善,增加更多的属性来应对更复杂的实际情况。
分析任务
有了上面的知识我们来看看我们的任务:
1 实现单行道限制
我们可以给连接增加属性叫“行驶方向限制”,有三个值,分别是:“正向通行”,“反向通行”,“双向通行”。然后在计算开销的时候,判断当前方向是否与连接节点的方向一致,然后根据“行驶方向限制”的值来决定是否允许通行,如果不允许则返回一个极大值代表开销。
2 实现左右拐弯限制
在实现单行线的基础上就可以实现拐弯限制,但是需要在数据制作上做文章:
我们不能用一条线来代表一个路段,而应该用并行且方向相反的两条线,这样拐弯的地方也自然变成了单行线的一部分了。
3 实现动态路况限制
这个在上一节介绍图的时候已经说明了不在赘述。
4 选择最短距离和最短时间
这个在上一节介绍图的时候已经说明了不在赘述。
代码实现
改程序要求:
1 路网数据是Shapfile格式保存的线段数据
2 属性必须提供两个:
id 整数
type 整数 只有三个值 1:正向通行;-1:反向通行;0:双向通行
程序采用Java编写,利用GeoTools 11软件包中的graph扩展实现图的搜索。程序启动后需要先选择路网数据,然后可见主界面,在主界面地图上点击设置起止位置。不同方向会选择不同道路,如图: