@@ -39,14 +39,18 @@ public class RouteService {
39
39
private static final Double INIT_OUTDOOR_DISTANCE = Double .MAX_VALUE ;
40
40
private static final Double MAX_OUTDOOR_DISTANCE = 0.003 ;
41
41
private static final Double MIN_OUTDOOR_DISTANCE = 0.0001 ;
42
+ private static final Double INDOOR_ROUTE_WEIGHT = 0.3 ;
42
43
43
44
/**
44
45
* 메인 경로탐색 메서드
45
46
*/
46
47
@ Transactional (readOnly = true )
47
48
public List <GetRouteRes > findRoute (LocationType startType , Long startId , Double startLat , Double startLong ,
48
- LocationType endType , Long endId , Double endLat , Double endLong , List <Conditions > conditions ){
49
+ LocationType endType , Long endId , Double endLat , Double endLong , List <Conditions > conditions ){
49
50
51
+ if (conditions == null ){
52
+ conditions = new ArrayList <>();
53
+ }
50
54
// 출발, 도착 노드 검색
51
55
Node startNode = getNodeByType (startType , startId , startLat , startLong );
52
56
Node endNode = getNodeByType (endType , endId , endLat , endLong );
@@ -64,10 +68,6 @@ public List<GetRouteRes> findRoute(LocationType startType, Long startId, Double
64
68
GetGraphRes graphRes = getGraph (buildingList , startNode , endNode , conditions );
65
69
DijkstraRes route = dijkstra (graphRes , startNode , endNode );
66
70
67
- // 경로를 하나만 반환한다면 경로가 없을 때 예외 처리
68
- if (route .getPath ().isEmpty ()) {
69
- throw new GlobalException (NOT_FOUND_ROUTE );
70
- }
71
71
routeRes .add (buildRouteResponse (route , startType == LocationType .BUILDING , endType == LocationType .BUILDING ));
72
72
73
73
// 베리어프리만 추가로 적용하는 경우(임시)
@@ -171,18 +171,32 @@ private List<Building> getBuildingsForRoute(Node startNode, Node endNode) {
171
171
}
172
172
173
173
/**
174
- * 출발/도착지에 직접적으로 연결된 건물들이 있는 경우 buildingList에 추가하는 메서드
174
+ * 출발/도착지에 연결된 건물들이 있는 경우 buildingList에 추가하는 메서드
175
+ * 연쇄적으로 연결된 건물들도 반영하도록(ex: 엘포관-백기-중지-SK미래관...) 수정
175
176
*/
176
- private void addConnectedBuildings (Building building , List <Building > buildingList ) {
177
- List <Long > connectedBuildingIds = connectedBuildingRepository .findConnectedBuildingsByBuilding (building );
178
- for (Long connectedBuildingId : connectedBuildingIds ) {
179
- if (buildingList .stream ()
180
- .noneMatch (existingBuilding -> existingBuilding .getId ().equals (connectedBuildingId ))) {
181
- buildingList .add (findBuilding (connectedBuildingId ));
177
+ private void addConnectedBuildings (Building startBuilding , List <Building > buildingList ) {
178
+ Queue <Building > queue = new LinkedList <>();
179
+ Set <Long > visited = new HashSet <>();
180
+
181
+ queue .add (startBuilding );
182
+ visited .add (startBuilding .getId ());
183
+
184
+ while (!queue .isEmpty ()) {
185
+ Building currentBuilding = queue .poll ();
186
+
187
+ List <Long > connectedBuildingIds = connectedBuildingRepository .findConnectedBuildingsByBuilding (currentBuilding );
188
+ for (Long connectedBuildingId : connectedBuildingIds ) {
189
+ if (!visited .contains (connectedBuildingId )) {
190
+ Building connectedBuilding = findBuilding (connectedBuildingId );
191
+ buildingList .add (connectedBuilding );
192
+ queue .add (connectedBuilding );
193
+ visited .add (connectedBuildingId );
194
+ }
182
195
}
183
196
}
184
197
}
185
198
199
+
186
200
/**
187
201
* 그래프 요소 찾기(node, edge 묶음)
188
202
* 노드 테이블의 String 인접 노드와 거리를 그래프로 변환
@@ -228,7 +242,9 @@ private GetGraphRes getGraph(List<Building> buildingList, Node startNode, Node e
228
242
graphEdge .put (node .getId (), new ArrayList <>());
229
243
}
230
244
for (int i = 0 ; i < nextNodeId .length ; i ++) {
231
- graphEdge .get (node .getId ()).add (new Edge (distance [i ], node .getId (), nextNodeId [i ]));
245
+ long weight = (conditions .contains (Conditions .INNERROUTE ) && node .getBuilding ().getId () != OUTDOOR_ID ) ? Math .round (distance [i ] * INDOOR_ROUTE_WEIGHT ) : distance [i ];
246
+ Edge edge = new Edge (distance [i ], weight , node .getId (), nextNodeId [i ]);
247
+ graphEdge .get (node .getId ()).add (edge );
232
248
}
233
249
}
234
250
return new GetGraphRes (graphNode , graphEdge );
@@ -257,12 +273,12 @@ else if (conditions.contains(Conditions.SHUTTLE)){
257
273
private DijkstraRes dijkstra (GetGraphRes graphRes , Node startNode , Node endNode ) {
258
274
List <Node > nodes = graphRes .getGraphNode ();
259
275
Map <Long , List <Edge >> edges = graphRes .getGraphEdge ();
260
- Map <Long , Long > distances = new HashMap <>(); // 출발 노드부터의 거리
261
- Map <Long , Long > previousNodes = new HashMap <>(); // 경로 반환을 위해 다시 거꾸로 추적하기 위한 노드 순서 저장
262
- PriorityQueue <NodeDistancePair > priorityQueue = new PriorityQueue <>(); // 시작 노드와 거리가 짧은 노드 순으로 선택 가능
263
- Set <Long > visitedNodes = new HashSet <>(); // 이미 방문한 노드 id 체크
276
+ Map <Long , Long > distances = new HashMap <>();
277
+ Map <Long , Long > previousNodes = new HashMap <>();
278
+ PriorityQueue <NodeDistancePair > priorityQueue = new PriorityQueue <>();
279
+ Set <Long > visitedNodes = new HashSet <>();
264
280
265
- // 모든 노드를 초기화
281
+ // 모든 노드 초기화
266
282
for (Node node : nodes ) {
267
283
if (node .equals (startNode )) {
268
284
distances .put (node .getId (), 0L );
@@ -273,27 +289,23 @@ private DijkstraRes dijkstra(GetGraphRes graphRes, Node startNode, Node endNode)
273
289
previousNodes .put (node .getId (), null );
274
290
}
275
291
292
+ // Dijkstra 실행 (weight 기준)
276
293
while (!priorityQueue .isEmpty ()) {
277
294
NodeDistancePair currentPair = priorityQueue .poll ();
278
295
Long currentNode = currentPair .node ;
279
296
280
- if (visitedNodes .contains (currentNode )) { // 방문한 노드면 패스
281
- continue ;
282
- }
297
+ if (visitedNodes .contains (currentNode )) continue ;
283
298
visitedNodes .add (currentNode );
284
299
285
- if (currentNode .equals (endNode .getId ())) { // 도착하면 종료
286
- break ;
287
- }
300
+ if (currentNode .equals (endNode .getId ())) break ;
288
301
289
- if (!edges .containsKey (currentNode )) continue ;
302
+ if (!edges .containsKey (currentNode )) continue ;
290
303
for (Edge edge : edges .get (currentNode )) {
291
304
Long neighbor = edge .getEndNode ();
292
305
Long currentDistance = distances .get (currentNode );
293
- if (currentDistance == null ) {
294
- continue ; // currentNode가 distances에 존재하지 않는 경우를 대비
295
- }
296
- Long newDist = currentDistance + edge .getDistance ();
306
+ if (currentDistance == null ) continue ;
307
+
308
+ Long newDist = currentDistance + edge .getWeight (); //weight 기반 탐색으로 수정
297
309
Long neighborDist = distances .get (neighbor );
298
310
if (neighborDist == null || newDist < neighborDist ) {
299
311
distances .put (neighbor , newDist );
@@ -303,21 +315,34 @@ private DijkstraRes dijkstra(GetGraphRes graphRes, Node startNode, Node endNode)
303
315
}
304
316
}
305
317
306
- // 경로 생성
318
+ //path 생성
307
319
List <Node > path = new ArrayList <>();
308
- Long finalDistance = distances .get (endNode .getId ());
309
- if (finalDistance .equals (INF )) {
310
- return new DijkstraRes (-1L , Collections .emptyList ()); // 경로가 존재하지 않을 때
311
- }
312
-
320
+ Node pathPrevNode = null ;
321
+ Long finalDistance = 0L ;
313
322
for (Long at = endNode .getId (); at != null ; at = previousNodes .get (at )) {
314
323
Node node = nodeRepository .findById (at ).orElseThrow (() -> new GlobalException (NOT_FOUND_ROUTE ));
324
+ if (pathPrevNode != null ) {
325
+ Edge edge = findEdge (edges , node .getId (), pathPrevNode .getId ());
326
+ finalDistance += edge .getDistance ();
327
+ }
315
328
path .add (node );
329
+ pathPrevNode = node ;
316
330
}
317
331
Collections .reverse (path );
318
332
333
+ //예외처리: path가 제대로 나오지 않는 경우. 즉, 경로가 존재하지 않는 경우
334
+ if (path .isEmpty () || !path .get (0 ).equals (startNode )) {
335
+ throw new GlobalException (NOT_FOUND_ROUTE );
336
+ }
337
+
319
338
return new DijkstraRes (finalDistance , path );
320
339
}
340
+ private Edge findEdge (Map <Long , List <Edge >> edges , Long from , Long to ) {
341
+ return edges .get (from ).stream ()
342
+ .filter (edge -> edge .getEndNode ().equals (to ))
343
+ .findFirst ()
344
+ .orElse (null );
345
+ }
321
346
322
347
/**
323
348
* 경로를 분할하고 응답 형식에 맞게 변환
@@ -328,7 +353,6 @@ private GetRouteRes buildRouteResponse(DijkstraRes route, boolean isStartBuildin
328
353
Long duration = route .getDistance ();
329
354
List <List <Node >> path = cutRoute (route .getPath ()); // 분할된 경로
330
355
331
- //시작, 끝이 건물인 경우 해당 노드 지우기
332
356
//시작, 끝이 건물인 경우 해당 노드 지우기
333
357
if (isStartBuilding ) {
334
358
// 첫번째 path의 길이에 따라 삭제 다르게 하기
@@ -338,7 +362,10 @@ private GetRouteRes buildRouteResponse(DijkstraRes route, boolean isStartBuildin
338
362
path .remove (0 );
339
363
}
340
364
}
341
- if (isEndBuilding ) path .get (path .size ()-1 ).remove (path .get (path .size ()-1 ).size ()-1 );
365
+
366
+ if (isEndBuilding && path .get (path .size ()-1 ).size () != 1 ) {
367
+ path .get (path .size ()-1 ).remove (path .get (path .size ()-1 ).size ()-1 );
368
+ }
342
369
343
370
List <PartialRouteRes > totalRoute = new ArrayList <>();
344
371
@@ -350,8 +377,8 @@ private GetRouteRes buildRouteResponse(DijkstraRes route, boolean isStartBuildin
350
377
List <List <Double >> partialRoute = convertNodesToCoordinates (thisPath , isOutside );
351
378
352
379
PartialRouteRes partialRouteRes = isOutside
353
- ? new PartialRouteRes (partialRoute ) // 야외 경로
354
- : new PartialRouteRes (thisPath .get (0 ).getBuilding ().getId (), thisPath .get (0 ).getFloor (), partialRoute ); // 실내 경로
380
+ ? new PartialRouteRes (partialRoute ) // 야외 경로
381
+ : new PartialRouteRes (thisPath .get (0 ).getBuilding ().getId (), thisPath .get (0 ).getFloor (), partialRoute ); // 실내 경로
355
382
356
383
// 부분 경로의 마지막 노드인 경우 설명 추가
357
384
if (i + 1 == path .size ()) {
@@ -384,8 +411,8 @@ private List<List<Node>> cutRoute(List<Node> route) {
384
411
385
412
// 새로운 건물로 이동할 때 & 체크포인트일때 & 외부에서 새로운 건물로 들어갈 때(입구 분리) 경로분할
386
413
if ((!thisNode .getBuilding ().equals (nextNode .getBuilding ()) && thisNode .getBuilding ().getId () != OUTDOOR_ID )
387
- || (thisNode .getType () != nextNode .getType () && thisNode .getType () == NodeType .CHECKPOINT )
388
- || (thisNode .getBuilding ().getId () == OUTDOOR_ID && nextNode .getType () == NodeType .ENTRANCE )) {
414
+ || (thisNode .getType () != nextNode .getType () && thisNode .getType () == NodeType .CHECKPOINT )
415
+ || (thisNode .getBuilding ().getId () == OUTDOOR_ID && nextNode .getType () == NodeType .ENTRANCE )) {
389
416
partialRoute .add (thisNode );
390
417
returnRoute .add (new ArrayList <>(partialRoute ));
391
418
partialRoute .clear ();
@@ -488,7 +515,7 @@ private String makeInfo(Node prevNode, Node nextNode){
488
515
private Building findLinkedBuilding (Node node ){
489
516
Long [] adjacentNodeIds = convertStringToArray (node .getAdjacentNode ());
490
517
return buildingRepository .findByNodeIdIn (adjacentNodeIds )
491
- .orElseThrow (() -> new AdminException (INCORRECT_NODE_DATA ,node .getId () + "번 노드에 연결된 건물이 없습니다" ));
518
+ .orElseThrow (() -> new AdminException (INCORRECT_NODE_DATA ,node .getId () + "번 노드에 연결된 건물이 없습니다" ));
492
519
}
493
520
494
521
/**
0 commit comments