旅游管理系统(包含旅游最短路径规划算法等,包含系统分析的各种uml图和界面图)
1.1問題定義
本次課程的設計題目是設計一個大型景區的管理系統,此系統是為了便于景區的管理以及提升游客的旅游體驗,更好的適應現如今日益發展的旅游業。
?
1.2問題分析
現有某景區需要開發一個景區信息管理系統,具體需求有:建立一個主程序應用菜單選項,方便用戶操作和選擇需要使用的功能。為了能夠適應不同景區的需求,系統需要能夠創建景點分布圖和輸出景點分布圖,對于創建,可以逐條添加景點信息和路徑信息;對于輸出,需要輸出景點分布圖的鄰接矩陣。而且,系統需要能夠制訂旅游景點導游線路策略,以便于游客游覽所有景點,即給出一個入口景點,生成一個導游線路圖;但是導游路線圖中可能含有回路,所以為了使導游線路圖能夠優化,需要判斷圖中有無回路,若有回路,則打印輸出回路中的景點。同時,在導游線路圖中,還需要為一些不愿按線路走的游客提供信息服務,比如從一個景點到另一個特定的景點。所以兩個景點之間的最短路徑和最短距離就變得很重要了,系統需要提供輸出兩個景點間的最短路徑和最短距離的功能。此外,在景區建設中,道路建設是其中一個重要內容,道路建設首先要保證能連通所有景點,但又要花最小的代價,修建道路的代價與它的里程相關。系統同樣需要提供景點搜索和景點排序的功能,游客通過輸入關鍵字來了解相關景點以便計劃好游覽地點和順序;而景點排序可以通過景點的歡迎度或景點的人流量來排序,讓游客對各個景點有個直觀的感受。最后,景區中還有固定容量的停車場,系統需要對到達車輛和離開車輛進行管理;當車輛到達時,如果停車場中還有剩余位置,則直接進入并開始計時,否則在候車道等待;當車輛離開時,在它之后進入的車輛必須先退出車場為它讓路,待該輛車開出大門外,其它車輛再按原次序進入車場,每輛停放在車場的車在它離開停車場時必須按它停留的時間長短交納費用。本系統面向管理員和游客,所以可以分為管理員功能和游客功能。
?
1.3研究意義
隨著科學技術的發展,人民生活水平的提高以及我國休假制度的完善,外出旅游成為了越來越多的人民度假的選擇。在這般背景前提下,造成了旅游景區在高峰期部分景點人流過大導致擁堵,從而影響景區形象和游客旅游體驗。從根本上說,這往往是因為景區服務不夠細致,管理不夠科學,效率不高所造成的。另一方面,游客人數的急劇增長所帶來的安全問題也日益凸顯起來。系統化、電子化、網絡化、智能化的景區管理系統也成為了日益迫切的需求,本項目就是在這樣的背景下提出的。針對此問題,所以研究和開發高質量、高性能的景區信息管理系統變得尤為重要,可以提高景區的管理水平,解放了繁重的體力勞動和腦力勞動,極大的改善了用戶服務的質量,提高了景區的信譽。
?
第二章?系統設計
2.1總體設計
2.1.1設計思想
系統采用前后端結合的方式,后端采用servlet運行核心算法,前端使用網頁進行結果動態展示,通信格式為JSON。整個系統的架構圖如下圖所示:
轉存失敗重新上傳取消
<1> 管理員功能:
(1)登錄:管理員使用賬戶密碼登錄。?????????
(2)公告發布:設計發布公告時將內容寫入一個文本文件,在游客登錄或管理員登錄時讀取這個文本文件并顯示在界面上。
(3)管理景點:景點信息的增加,刪除和修改的功能。
(4)管理道路:實現道路的插入和刪除的功能。
(5)輸出停車場信息:顯示停車場信息,包括等待車輛,停車場空位。
?
<2> 游客功能:
(1)景點查詢和排序:可以根據自己的需求對景區進行各種信息查詢并對景點進行一定順序上的排序。
(2)景點最短路徑:系統還應能夠根據游客輸入的兩個景點的名稱來輸出兩地之間的最短路徑和最短距離。
(3)輸出導游路線圖:系統還能為游客輸出導游路線圖。
?
2.1.2數據結構
- 圖的存儲結構
public class ALGraph {
private int arcNum; //景點數量
???? private int vetNum; //路的數量
???? private List<ArcNode> nodes; //存儲景點的list
···
??? }
- 景點結點
public class ArcNode {
private String name; //景點名稱
private String des; //景點描述
private int pop; //景點歡迎度
private boolean hasRest; //有無休息區
private boolean hasToilet; //有無公廁
private VNode first; //景點的第一條邊
···
}
- 邊結點
?
public class VNode {
private int index; //另一個景點在景點數組中的位置
private int dist; //兩個景點的距離
private int time; //所需時間
private VNode next; //與頭結點相連的下一條邊
···
}
- 汽車
public class Car {
private String number; //汽車車號
private Date ar_time; //汽車到達時間
···
}
- 棧
public class Stack<T> {
private T[] mArray;
private int size;
?
public void push(T val){
···
}
?
public void pop(){
···
}
?
public T peek(){
···
}
}
- 隊列
public class Queue {
private CarNode phead; //汽車結點的鏈表頭
private int size; //隊列的元素個數
?
public void add(Car car){
···
}
?
public void pop(){
···
}
?
public CarNode front(){
···
}
}
2.1.3用戶界面
轉存失敗重新上傳取消
轉存失敗重新上傳取消
轉存失敗重新上傳取消
轉存失敗重新上傳取消
?
轉存失敗重新上傳取消
?
轉存失敗重新上傳取消
?
?
2.2程序設計
2.2.1類圖
轉存失敗重新上傳取消
2.2.2流程圖
轉存失敗重新上傳取消
第三章?系統實現與調試
3.1 導游線路圖的創建
建立無向帶權圖,輸入頂點和邊的信息,輸出鄰接鏈表G。因無向邊的原因,輸入一條邊時構建兩條點。對于導游線路圖的創建,該系統直接從服務器端存儲的Arc.txt中讀取所有景點信息和路徑信息。如果需要更換旅游圖,則直接在txt文件中修改即可。
讀入數據的代碼段為:
for(int?i=1; i<=graph.getArcNum(); i++){
tmp = reader.nextLine();
infos?= tmp.split(" ");
name = infos[0];
des = infos[1];
pop = Integer.parseInt(infos[2]);
hasRest = ((infos[3].equals("1"))?true:false);
hasToilet = ((infos[4].equals("1"))?true:false);
graph.getNodes().add(new?ArcNode(name, des, pop, hasRest, hasToilet));
}
對于導游路線圖的輸出,從鄰接鏈表轉換成鄰接矩陣,并輸出鄰接矩陣。
輸出數據的代碼段為:
for(int?i=0; i<graph.getArcNum(); i++){
System.out.print(graph.getNodes().get(i).getName()+" ?");
for(int?j=0; j<graph.getArcNum(); j++){
if(i == j){
System.out.print("0 ?");
continue;
}
int?dis = isContact(i, j);
System.out.print(dis + " ?");
}
System.out.println();
}
將鄰接鏈表中的景點和路徑信息顯示在前端html頁面的中,即在前端顯示模擬的導游路線圖和景點信息。
HTML5中有canvas這個網頁元素,可以用JavaScript代碼在canvas中直接繪制圓、直線等幾何圖形。所以選用了該元素,并使用JavaScript繪制了景點、路徑、路徑長度以及各個景點的信息。
context.beginPath();
//畫圓
context.arc(xPosition,yPosition,30,0,Math.PI*2,true);
//畫線
context.moveTo(x1Position,y1Position);
context.lineTo(x2Position,y2Position);
//添加文字
context.fillText(dist, xPosition, yPosition);
context.closePath();
context.stroke();
3.2輸出導游路線圖及其圖中的回路部分
TourSystem中可輸出旅游景點導游線路,輸入一個景點作為入口,建立一個導游線路圖,導游線路圖用有向圖表示。遍歷采用深度優先遍歷,這也比較符合游客心理。
深度優先遍歷的關鍵代碼為:
public?List<Integer> DFSTraverse(String start){
Stack<Integer> traverseNodes = new?Stack<Integer>(Integer.class, graph.getArcNum());
startIndex?= getPos(start);
traverseNodes.push(startIndex);
while(!traverseNodes.isEmpty() && !hasAllVisited()){
int?arcNodeIndex = traverseNodes.peek();
visited[arcNodeIndex] = true;
VNode vNode =?graph.getNodes().get(arcNodeIndex).getFirst();
while(vNode != null){
if(!visited[vNode.getIndex()]){
traverseNodes.push(vNode.getIndex());
break;
}
vNode = vNode.getNext();
}
if(vNode == null){
traverseNodes.pop();
}
tourIndexList.add(arcNodeIndex);
}
?
return?tourIndexList;
}
為優化導游線路圖功能,可通過拓撲排序的方法判斷圖中有無回路,若有回路,則打印輸出回路中的景點。拓撲排序只在有向無環圖中有效,這里針對輸出的有向路線圖。過程如下:(1)計算有向圖中每個頂點的入度,存儲在數組中,將所有入度為0的頂點入隊;(2)遍歷隊列,在遍歷的過程中,將出隊的頂點所有相鄰的頂點的入度減1,如果有入度減為0的頂點則繼續入隊,否則繼續遍歷隊列剩余元素;(3)遍歷入度數組,若入度不為0,則該頂點在回路中。
拓撲排序的關鍵代碼為:
while(!queue.isEmpty()){
int?ind = queue.poll();
VNode node = directGraph.getNodes().get(ind).getFirst();
while(node != null){
indegree[node.getIndex()]--;
if(indegree[node.getIndex()] == 0){
queue.offer(node.getIndex());
}
node = node.getNext();
}
}
拓撲排序過程中,首先會將所有入度為0的頂點入隊,然后才可以繼續進行隊列元素的提取和添加。在本功能實現過程中,通過路線圖輸出過程中建立起來的有向圖里,沒有入度為0的頂點,所以拓撲排序無法正常進行。
將入口頂點的入度置為0,然后將其放入隊列中,這樣就能正常進行拓撲排序了。比如北門是起始點,則將北門這個頂點的入度置為0,然后放入隊列。
3.3輸出兩個景點之間最短路徑和最短距離
Dijkstra算法是典型的最短路徑算法,用于計算一個節點到其他所有節點的最短路徑。設G=(V,E)是一個帶權有向圖,把圖中頂點集合V分成兩組,第一組為已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,以后每求得一條最短路徑 , 就將加入到集合S中,直到全部頂點都加入到S中,算法就結束了),第二組為其余未確定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點v到S中各頂點的最短路徑長度不大于從源點v到U中任何頂點的最短路徑長度。此外,每個頂點對應一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點為中間頂點的當前最短路徑長度。
Dijkstra的關鍵代碼為:
public?void?dijkstra(String source, String des){
sourceIndex?= getPos(source);
desIndex?= getPos(des);
//初始化最短距離數組為INF
for(int?i=0; i<graph.getArcNum(); i++){
dis[i] = (i==sourceIndex?? 0 : Constants.INF);
}
?
for(int?i=0; i<graph.getArcNum(); i++){
int?minPos = -1, m = Constants.INF;
for(int?j=0; j<graph.getArcNum(); j++){
if(vis[j]==0 && dis[j]<m){
m = dis[minPos=j];
}
}
vis[minPos] = 1;
for(int?j=0; j<graph.getArcNum(); j++){
if(vis[j]==0 && dis[minPos]+getLength(minPos, j)<dis[j]){
dis[j] = dis[minPos]+getLength(minPos, j);
fath[j] = minPos;
}
}
}
}
通過Dijkstra算法直接計算兩點間的最短距離比較容易,如何保存最短路徑中的頂點信息則相對困難一些。
通過設定一個fath數組,用來記錄最短路徑尋找過程中,尋找路線確定的頂點信息。當到達某點的距離需要更新時,fath數組則用來記錄邊的起點信息。最后通過目標點的坐標依次遍歷fath數組,則可得到逆序的路線圖。
3.4輸出道路修建規劃圖
在景區建設中,道路建設是其中一個重要內容。道路建設首先要保證能連通所有景點,但又要花最小的代價,可以通過求最小生成樹來解決這個問題。首先將圖中所有的邊按其權值大小排序,然后按由小到大的次序順序選取這些邊,若選邊后不形成回路,則保留作為一條邊,若形成回路則除去.依次選夠(n-1)條邊,即得最小生成樹。(n為頂點數)
最小生成樹的關鍵代碼為:
//用于保存已有最小生成樹中每個頂點在該最小生成樹中的終點
int[] ends = new?int[graph.getVetNum()];
//用于保存結果最小生成樹的邊
VData[] results = new?VData[graph.getVetNum()];
?
//獲取圖中所有的邊
VData[] edges = getEdges();
//將邊按照權的大小進行排序(從小到大)
sortEdges(edges);
//對所有邊進行遍歷
for(int?i=0; i<graph.getVetNum(); i++){
int?m = getEnd(ends, edges[i].getStart());?//獲取起點在已有最小生成樹中的終點
int?n = getEnd(ends, edges[i].getEnd()); //獲取該邊終點在已有最小生成樹中的終點
//如果m!=n,說明在已有最小生成樹中添加該邊不會形成回路
if(m != n){
ends[m] = n;
results[index++] = edges[i];
}
}
最小生成樹算法在選邊的過程中要判斷添加該邊后是否會形成回路,若形成回路則出去,不形成則屬于最小生成樹里的一條邊。那么如何判斷是否會形成回路。
使用并查集的思想。首先將頂點劃分到不同集合中,每個集合中的頂點表示一個無回路的連通分量;然后當選取一條邊時,若它的兩個頂點屬于不同的集合,則這條邊保留,并把兩個頂點所在的集合合并成一個集合。如果選取的一條邊的兩個頂點屬于同一個集合,則該邊舍棄。
3.5查找及排序
對于景點查找功能,本系統采用KMP匹配算法。KMP字符串模式匹配通俗點說就是一種在一個字符串中定位另一個串的高效算法。首先求得模式串中每個字符的next[j]值;然后進行模式匹配。此算法的思想是直截了當的:將主串S中某個位置i起始的子串和模式串T相比較。即從j=0起比較S[i+j]與T[j],若相等,則在主串S中存在以i為起始位置匹配成功的可能性,繼續往后比較(j逐步增1),直至與T串中最后一個字符相等為止,否則改從S串的下一個字符起重新開始進行下一輪的"匹配",即將串T向后滑動一位,即i增1,而j退回至0,重新開始新一輪的匹配,此時j=0。
KMP匹配算法的關鍵代碼為:
int[] K = calculateK(keyword);
?
int?i = 1, j = 1;
while(i<=doc.length() && j<=keyword.length()){
if(j==0 || newDoc.charAt(i)==newKeyword.charAt(j)){
i++;
j++;
}else{
j = K[j];
}
}
對于排序功能,由于快速排序算法時間復雜度為O(N*logN),所以本系統采用快速排序算法。算法描述如下:從數列中取出一個數作為基準數,然后開始分區過程,將比基準數大的數全放在它的右邊,小于或等于它的數全放在它的左邊。再對左右區間分別重復第二步,直到各區間只有一個數,算法結束。
快速排序算法的關鍵代碼為:
public?void?quickSort(int[] a, int?l, int?r) {
if?(l < r) {
int?i,j,x;
?
i = l;
j = r;
x = a[i];
while?(i < j) {
while(i < j && a[j] > x) j--; // 從右向左找第一個小于x的數
if(i < j) a[i++] = a[j];
while(i < j && a[i] < x) i++; // 從左向右找第一個大于x的數
if(i < j) a[j--] = a[i];
}
????????? a[i] = x;
????????? quickSort(a, l, i-1); /* 遞歸調用 */
????????? quickSort(a, i+1, r); /* 遞歸調用 */
???? }
}
???最初字符串匹配算法采用的方法是樸素字符串匹配算法,即目標字符串與原字符串進行比較,如果出現不相同的字符,就將目標字符串前移到出現不同字符的位置然后重新進行比較,可是這樣的算法效率太低。
考慮到KMP匹配算法的時間復雜度為O(n+m),所以其效率要比樸素字符串匹配算法高效很多,最終選擇使用KMP匹配算法用來搜索功能。
3.6輸出車輛的進出信息
為實現任務要求的車輛停放要求,本項目自定義實現了棧和隊列兩種數據結構。有車輛進入停車場時,首先判斷停車場是否為已滿;如果已經滿了,就將車加入候車道中,否則就添加車到停車場中。
車輛添加關鍵代碼為:
//判斷停車場是否滿了,若滿了則添加車到候車道,否則直接添加
if(parking.isFull()){
shortcut.add(new?Car(number, null));
}else{
parking.push(new?Car(number, new?Date()));
}
汽車駛出停車場時,先將目標車輛后面的車壓入暫時停車場中;然后目標車輛離開,算出停留時間和應繳費用;接著將暫時停車場棧中的車重新壓入到停車場棧中;最后,如果候車道中停放有車輛,就將候車道隊列最前面的車移出來壓到內部棧中并修改駛入時間。
車輛離開關鍵代碼為:
//查找指定車牌號的car,將其后的car停入暫時的停車場
while(parking.peek().getNumber() == number){
tempParking.push(parking.peek());
parking.pop();
}
parking.pop();
//將暫時停車場中的車重新壓入停車場棧
while(!tempParking.isEmpty()){
parking.push(tempParking.peek());
tempParking.pop();
}
//若候車道中停放有車輛,就移出最前面的車
if(!shortcut.isEmpty()){
parking.push(shortcut.front().getCar());
shortcut.pop();
}
當汽車離開停車場時,動態顯示汽車進入暫時停車場、汽車離開和候車道中的汽車進入停車場的過程。
在后臺算法運行的過程中,棧和隊列每更新一次,就將兩個棧和一個隊列中的車輛信息記錄一次。然后生成JSON字符串返回給網頁端,網頁端通過JavaScript有間隔的更新每次記錄的信息,即可生成動態過程。
第四章?系統測試
4.1測試用例
- 輸入為空
- 輸入景點分布圖中不存在的景點名稱
- 輸入為空
- 輸入景點分布圖中不存在的名稱
- 輸入相同的起始點
- 車場初始化前進行車輛到達或車輛離開操作
- 初始化輸入為負數或小數
- 車輛到達和車輛離開輸入為空
- 車輛到達輸入已存在車牌號
- 車輛離開輸入不存在車牌號
4.2測試結果
- 彈出提示框,顯示起點輸入不能為空
- 彈出提示框,顯示景點不存在,請重新輸入
- 彈出提示框,顯示起始點輸入不能為空
- 彈出提示框,提示起點或終點名稱輸入錯誤,請重新輸入
- 后臺報錯,拋出異常
?
?
5.3 主要創新點
?
?
5.4 遇到的困難
- 彈出提示框,提示請先進行車場初始化
- 彈出提示框,提示請輸入正確格式的數
- 彈出提示框,提示信息不能為空
- 彈出提示框,提示車牌號已存在,請輸入其它車牌
- 彈出提示框,提示車場中無此車牌號,請重新輸入
-
第五章?結論
5.1實現結果
?
?
正在上傳…重新上傳取消
?
轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消
?
5.2系統功能
- 創建和輸出分布圖
- 輸出線路圖和回路
- 景點間最短路徑
- 道路修建圖
- 景點查找和排名
- 停車場管理
- 發布公告
- 將算法部署在Tomcat中,算法運行結果在網頁端顯示
- 網頁端顯示過程均為動態展示過程
- 搜索算法使用KMP匹配算法對景點名稱和景點描述進行搜索
- 輸出回路時,若有多條回路,則可以全部輸出
- 某些算法的實現過程,比如KMP匹配算法的計算next數組
- 路線圖的繪制方法
?
總結
以上是生活随笔為你收集整理的旅游管理系统(包含旅游最短路径规划算法等,包含系统分析的各种uml图和界面图)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql无法找到事件id100描述_解
- 下一篇: NLPIR ICTCLAS2015分词系