기존 네비게이션은 경로 이탈 시 따로 처리를 해주지 않았다.
그 결과 경로를 이탈해도 다음 안내지점을 고치지 않고, 말도 안되는 경로로 경로 안내를 해 주었다.
아래의 사진을 보면 알 수 있다.
이 문제를 해결하기 위해 다음과 같은 기능을 가진 코드를 작성하기로 했다.
1. 경로 이탈 판정을 해주는 코드
2. 경로 이탈시 새로운 경로 탐색 후 추천
2번 기능을 구현하기 위해서는 일반 네비게이션과 랜덤 네비게이션,
그리고 기록을 바탕으로 한 네비게이션을 따로 따로 구현해야 했다.
이 중 나는 랜덤 네비게이션, History 네비게이션 경로 이탈 처리 코드를 개발하기로 했다.
경로 이탈 판정 코드
경로 이탈 판정 코드는 모두 calculation.js에 작성되어 있다.
아래와 같은 코드를 작성해서 위치 변경시 action에 추가해주었다.
if ((((PastToPos + CurrectToPos) - roadPartLength) * 1000) > offetUserRadius)
{
outOfPath(lat, lng);
return;
}
offetUserRadius 값에 따라 경로 이탈 판정 범위를 바꿀 수 있고, 우리는 20미터로 설정했다.
또한 outOfPath()라는 코드를 추가해 일반 네비게이션과 그 외 네비게이션 경로 이탈 시 어떤 메서드를 실행시킬 것인지 결정하게 했다.
function outOfPath(lat, lng)
{
if (pathType() === 'live')
{
remakeNavi(lat, lng);//응답시 resetNavi() 실행호출 준비
}
else
{
remakeRandomNavi(lat, lng);
}
console.warn("경로 이탈");
}
경로 이탈(Random, History)시 작동 코드
1. 필요한 전역 변수 추가하기
staticVariable.js에 다음과 같은 코드를 추가했다.
var waypointCount = 0;
var offCourseCount = 0;
waypointCount는 경유지를 지날때마다 1 씩 증가하며,
offCourseCount는 0일때는 경로이탈 한적 없음, 1이상이면 경로이탈 경험 있음을 의미한다.
2. 주 기능 JS 코드 작성
navi.js에 아래와 같은 코드를 추가했다. 경로 이탈 시 카카오 API를 이용해 새로운 경로를 생성해준다.
카카오 API를 이용해 새로운 경로를 생성해준 이유는 우리는 도로에 대한 정보를 가지고 있지 않기 때문에
직접 경로 탐색을 할 수는 없기 때문이다.
function remakeRandomNavi(lat, lng) {
setToken();
let currentAddress = "";
fetch(
'https://dapi.kakao.com/v2/local/geo/coord2address?x=' + lng + '&y=' + lat,
{
headers: { Authorization: 'KakaoAK 4752e5a5b955f574af7718613891f796' }, //rest api 키
}
)
.then((response) => response.json())
.then((data) => {
currentAddress = data.documents[0].address.address_name;
console.log(data);
let destinationLatitude = destinationLocation.lat;
let destinationLongitude = destinationLocation.lng;
if (type === 'save' && offCourseCount === 0) {
// routeData.guides 배열의 맨 마지막 요소가 목적지
let destinationGuide = routeData.guides[routeData.guides.length - 1];
destinationLatitude = destinationGuide.y;
destinationLongitude = destinationGuide.x;
}
console.log(routeData.guides
.filter(guide => guide.type === 1000));
offCourseCount++;
let waypointsX, waypointsY;
waypointsX = "";
waypointsY = "";
let isexist = false;
for (let i = naviInfo_ProcessIndex; i < routeData.guides.length; i++)
{
if (routeData.guides[i].type === 1000 && routeData.guides[i].road_index === -1) {
isexist = true;
waypointsX += routeData.guides[i].x + " ";
waypointsY += routeData.guides[i].y + " ";
}
}
if (!isexist)
{
waypointsX = "0";
waypointsY = "0";
}
console.log("주소: " + currentAddress);
console.log("경유지 y: " + waypointsY + ", 경유지 x:" + waypointsX);
console.log("목적지 y: " + destinationLatitude + ", 경유지 x:" + destinationLongitude);
{
fetch('/api/offCourse?currentAddress=' + currentAddress
+ '&destinationY=' + destinationLatitude + '&destinationX=' + destinationLongitude
+ '&waypointsY=' + waypointsY + '&waypointsX=' + waypointsX)
.then(response => response.json())
.then(data => {
responseData = data;
adapt_KakaoResponseToRouteData(data);
makeLiveMap(data)
clearNavi();
})
.catch(error => {
console.error('Error:', error);
});
}
});
}
각각의 기능을 설명하자면 아래와 같다.
- 현 위치를 유효한 위치로 전환해주기.
이 기능이 필요한 이유는 현 위치를 출발 주소로 하고 새로운 길찾기를 할 시 유효한 위치로 판정되지 않으면서,
반복적으로 경로 이탈 판정을 내려 새로운 길을 찾기 때문이다.
발생한 오류는 아래와 같다.
분명 형식상 오류는 없지만, Bad Request가 뜨며, 계속 경로이탈 판정을 내린다.
그래서 조금은 부정확하더라도, 현 위치를 유효한 좌표로 바꿔주기로 했고, 그 결과 아래 코드가 작성되었다.
fetch( 'https://dapi.kakao.com/v2/local/geo/coord2address?x=' + lng + '&y=' + lat, { headers: { Authorization: 'KakaoAK 4752e5a5b955f574af7718613891f796' }, //rest api 키 }
- 현 주소와 목적지 좌표 data에서 가져오기
.then((response) => response.json()) .then((data) => { currentAddress = data.documents[0].address.address_name; console.log(data); let destinationLatitude = destinationLocation.lat; let destinationLongitude = destinationLocation.lng;
이 코드가 이렇게 작성된 이유는 staticVariable.js에서 data를 우리가 쓰기 편하게 바꿔놓기 때문이다.
currentAddress는 위 데이터에서 가져올 수 있고, 목적지 좌표는 위 형식에 맞게 이미 저장되어 있다. - 현 Data 형식 알아내기
if (type === 'save' && offCourseCount === 0) { // routeData.guides 배열의 맨 마지막 요소가 목적지 let destinationGuide = routeData.guides[routeData.guides.length - 1]; destinationLatitude = destinationGuide.y; destinationLongitude = destinationGuide.x; } offCourseCount++;
만일 위의 if 문에 걸린다면 data의 형식이 변환된 형식이 아닌 KakaoRouteAllResponseDto그대로 저장되어 있다.
고로 목적지의 저장 형식이 다르므로 위와 같이 if문을 이용해 따로 저장해준다. - 경유지 문자열로 묶어서 한번에 반환해주기
경유지를 띄어쓰기로 구분한 문자열로 묶어서 한번에 반환해준다.
Data의 모든 guides를 순회하며 type가 1000이고, road_index가 -1인 guides만 경유지로 판정해준다.let waypointsX, waypointsY; waypointsX = ""; waypointsY = ""; let isexist = false; for (let i = naviInfo_ProcessIndex; i < routeData.guides.length; i++) { if (routeData.guides[i].type === 1000 && routeData.guides[i].road_index === -1) { isexist = true; waypointsX += routeData.guides[i].x + " "; waypointsY += routeData.guides[i].y + " "; } } if (!isexist) { waypointsX = "0"; waypointsY = "0"; }
저장된 데이터에는 type가 1000인 안내 지점이 경유지고 경유지는 road_index가 -1과 0인 경유지 두개로 나뉜다.
고로 if 문을 사용해서 중복되지 않는 경유지만 저장할 수 있다. - 새로운 데이터 생성, 전역변수에 저장
위 과정을 거쳐 나온 정보들을 이용해 요청을 만들어 준다. 그 후 데이터는 responseData와 routeData에 저장된다.
{ fetch('/api/offCourse?currentAddress=' + currentAddress + '&destinationY=' + destinationLatitude + '&destinationX=' + destinationLongitude + '&waypointsY=' + waypointsY + '&waypointsX=' + waypointsX) .then(response => response.json()) .then(data => { responseData = data; adapt_KakaoResponseToRouteData(data); makeLiveMap(data) clearNavi(); }) .catch(error => { console.error('Error:', error); }); }
3. 주기능 JAVA 코드 작성
- Controller
JS에서 보내주는 형식에 맞게 작성한다.
@GetMapping("/offCourse") public ResponseEntity<KakaoRouteAllResponseDto> getOffCourseWays(@RequestParam String currentAddress, @RequestParam double destinationY, @RequestParam double destinationX, @RequestParam String waypointsY, @RequestParam String waypointsX) { KakaoRouteAllResponseDto response = randomOffCourseService.requestOffCourseSearch(currentAddress, destinationY, destinationX, waypointsY, waypointsX); if (response == null) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); // 적절한 HTTP 상태 코드로 응답 } return new ResponseEntity<>(response, HttpStatus.OK); }
- Service
RealRandomRouteSearchService 클론코딩 결과이다.
약간 특이한 점을 뽑자면 Queue를 이용해 들어온 경유지 좌표를 잘라서 저장후 다시 이용하는 방식인데
흠.. 지금 보니 속도에 영향을 조금 미칠 수도 있을 것 같다.
public KakaoRouteAllResponseDto requestOffCourseSearch(String currentAddress, double destinationY, double destinationX, String waypointsY, String waypointsX) { if (ObjectUtils.isEmpty(currentAddress) || ObjectUtils.isEmpty(destinationY) || ObjectUtils.isEmpty(destinationX)) return null; log.info("랜덤 네비게이션 경로 이탈 서비스 실행"); DocumentDto origin = kakaoAddressSearchService.requestAddressSearch(currentAddress).getDocumentDtoList().get(0); double originY = origin.getLatitude(); double originX = origin.getLongitude(); // "위도,경도" 형식의 문자열 생성 Map<String,Object> uriData = new TreeMap<>(); uriData.put("origin",new DocumentDto( "현 출발지" , originY, originX)); uriData.put("destination",new DocumentDto("이전 목적지", destinationY, destinationX)); if (!waypointsX.equals("0")) { List<DocumentDto> waypoints = new ArrayList<>(); Queue<Double> waypointsYQueue = new LinkedList<>(); String[] waypointsYArray = waypointsY.split(" "); for (String coordinate : waypointsYArray) { double waypointY = Double.parseDouble(coordinate); waypointsYQueue.add(waypointY); // 예외처리 } Queue<Double> waypointsXQueue = new LinkedList<>(); String[] waypointsXArray = waypointsX.split(" "); for (String coordinate : waypointsXArray) { double waypointX = Double.parseDouble(coordinate); waypointsXQueue.add(waypointX); // 예외처리 } while (!waypointsYQueue.isEmpty()) { DocumentDto waypoint = new DocumentDto("이전 경유지", waypointsYQueue.poll(), waypointsXQueue.poll()); waypoints.add(waypoint); } uriData.put("waypoints", waypoints); } uriData.put("priority","RECOMMEND"); URI uri = URI.create("https://apis-navi.kakaomobility.com/v1/waypoints/directions"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set(HttpHeaders.AUTHORIZATION, "KakaoAK " + kakaoRestApiKey); HttpEntity <Map<String,Object>> httpEntity = new HttpEntity<>(uriData,headers); return restTemplate.postForEntity(uri,httpEntity,KakaoRouteAllResponseDto.class).getBody(); }
이렇게 내가 맡은 경로이탈 기능이 완성되었다. 이제 경로를 이탈해도 주행에 지장없이 새로운 경로를 만들어 줄 수 있다.
'프로젝트 > RanDrive' 카테고리의 다른 글
기능 개선(예외처리) (0) | 2023.11.02 |
---|---|
기능 개선(새로운 랜덤 길찾기 알고리즘 프론트 코드 작성) (1) | 2023.11.01 |
기능 개선(JMeter) (0) | 2023.10.30 |
기능 개선(랜덤 길찾기 알고리즘) (0) | 2023.10.28 |
https 배포 (0) | 2023.10.28 |