1. 소개
예전 포스팅에서 자료조사를 했습니다. 한 요청에 대량의 경로탐색이 필요한 프로젝트 특성상 외부 API 사용은 비용의 문제가 있었습니다. 그래서 자체 대중교통 경로탐색이 필요했습니다. 자료조사를 통해 GTFS(대중교통 스케줄), OSM(지도 데이터)를 찾았고 추가로 OTP(경로탐색 오픈 소스)를 찾았습니다. 이제 필요한 것들을 다 모았으니 구현을 해봅시다
`최소 16GB 이상의 메모리를 사용하시는 걸 추천합니다. 되도록이면 32GB`
`현재 서비스중인 kakao, naver와의 예상 시간이 꽤 차이가 나 실사용은 어렵습니다`
2. 사전 준비
대중교통 경로 탐색에는 3가지가 필요합니다.
- OSM(Open Street Map) : 공간 정보를 위한 지도 데이터
- OTP(Open Trip Planner) : 지도에서 경로 탐색을 위한 탐색 엔진
- GTFS(General Transit Feed Specification) : 탐색 엔진의 재료가 되는 대중교통 스케줄 데이터
OSM(Open Street Map)
https://www.openstreetmap.org/#map=15/37.58407/126.96406
오픈스트리트맵
오픈스트리트맵 (OpenStreetMap)은 여러분과 같은 사람들이 만들어, 개방형 라이선스에 따라 자유롭게 사용할 수 있는 세계 지도입니다.
www.openstreetmap.org
osm은 지리 공간 데이터베이스로 전세계의 공간 데이터를 확인할 수 있습니다. 자유롭게 사용, 수정이 가능하며 사용자가 직접 도로, 건물, 지형, 대중교통 정보를 추가하고 업데이트할 수 있습니다.
해당 링크로 카카오 맵, 네이버 맵과 같은 지도 서비스를 사용해 볼 수 있고 재밌는 점은 군사지역 정보도 표시되어있다는 점입니다. 오픈 소스이기 때문에 가능한 일이라고 생각합니다.
오픈스트리트맵 이용하기
전 세계의 지리공간 정보를 날것 그대로 보고, 원하는 대로 조작하고, 복잡한 저작권 문제 없이 (‘ⓒOpenStreetMap 기여자’만 적어서) 배포하는 것은 오픈스트리트맵만의 특권입니다.
osm.kr
한국 osm 홈페이지로 해당 링크를 통해 파일을 구할 수 있으며 최종 다운로드 링크는 다음과 같습니다.
https://download.geofabrik.de/asia/south-korea-latest.osm.pbf
GTFS(General Transit Feed Specification)
대중교통 스케줄 데이터로 특정 버스의 이동 데이터들을 기록한 표준 규격입니다. 예를 들어 1번 버스가 몇시에 출발하며 어디 차고지에서 어떤 경로로 움직이며 각 정류장에서의 승하차 시간등을 기록한 상세한 스케줄 데이터입니다.
현재 최신 GTFS는 2023년도이며 국가교통DB에서 `교통분석자료신청`을 통해 다운받을 수 있습니다.
https://www.ktdb.go.kr/www/contents.do?key=202
자료신청
여객O/D, 화물O/D, 교통유발원단위, 교통량, 교통분석용 네트워크, 교통주제도 데이터는 누구나 자료 신청을 할 수 있고, 이용자 만족도 조사 후에 자료를 다운받을 수 있습니다. 신청서는 사용목
www.ktdb.go.kr
다운받아 압축을 풀면 설명서와 함께 데이터셋을 얻을 수 있습니다.
google에선 위와 같은 정적 gtfs와 실시간 gtfs를 제공하지만 아쉽게도 한국의 데이터는 없어서 참 아쉽습니다. 그래도 한국교통연구원에서 `gtfs기반 대중교통 네트워크 구축 공고`를 확인했는데 나중에 일반이도 해당 네트워크를 이용할 수 있으면 좋겠습니다.
OTP(Open Trip PLanner)
https://gtfs.org/resources/software-for-creating-apis/
Software for Creating APIs - General Transit Feed Specification
Software for Creating APIs Software that you can set up to provide an API to transit and multimodal data. GraphHopper Routing Engine Open source routing engine for OpenStreetMap. Use it as Java library or server. gtfs-server - A web server, written in Rust
gtfs.org
osm, gtfs 홈페이지에서 경로 탐색을 위한 여러 툴들을 소개해줍니다.
An open source platform for multi-modal and multi-agency journey planning, as well as returning information about a multi-modal graph (using data sources such as GTFS and OpenStreetMap)
gtfs, osm를 사용한 멀티 모달 여행 계획을 지원한다고되어있습니다. 멀티 모달 플래닝은 대중교통, 걷기 등 혼합 경로 탐색입니다.
otp는 최초 build 시 osm, gtfs데이터를 사용해 교통 네트워크 그래프를 만들기 때문에 많은 메모리를 요구하고 약 10분 정도의 시간이 소요됩니다. 저는 기본 설정으로 빌드에 실패하고 12gb를 설정하니 성공했습니다.
https://repo1.maven.org/maven2/org/opentripplanner/otp/
Central Repository: org/opentripplanner/otp
repo1.maven.org
해당 사이트에서 원하는 버전을 다운받을 수 있습니다. 최신버전을 다운받으면 자바 버전을 21이상 사용해야하는데 현재 프로젝트에서 17버전을 사용하기 때문에 2.4.0 버전을 다운받았습니다. 파일은 *.jar 형식으로 java로 작성되어있습니다.
https://docs.opentripplanner.org/en/latest/Basic-Tutorial/#get-java
Basic Tutorial - OpenTripPlanner 2
OpenTripPlanner Basic Tutorial This page should allow you to set up and test your own OTP2 server. If all goes well it should only take a few minutes! Get Java As a Java program, OTP must be run within a Java virtual machine (JVM), which is provided as par
docs.opentripplanner.org
otp 공식 홈페이지로 설치 방법과 여러 api를 확인할 수 있습니다.
이제 폴더를 하나 만들고 다운받은 파일들을 모아줍니다. 바로 빌드 커맨드를 입력해도되지만 추가 경로 탬색에 도움이되는 설정을 추가합니다.
{
"routingDefaults": {
"numItineraries": 3, // 최대 경로 갯수
"walkSpeed": 1.3, // 걷기 속도 m/s
"maxWalkDistance": 1000 // 환승 시 최대 도보 거리 km
}
}
route-config.json이라는 이름으로 저장 후 이제 커맨드를 입력해 빌드를 해봅시다.
java -Xmx16G -jar otp-2.4.0-shaded.jar --port 9090 --build --save --serve .
build 커맨드입니다. 저는 32gb메모리를 가지고있어 넉넉하게 16로 설정했습니다. 12gb정도로도 빌드 가능하지만 현재 메모리 사용량을 보고 메모리를 넉넉히 확보해주세요.
- -Xmx : otp 메모리 할당 설정
- --port : 포트 설정
- --save : 생성된 네트워크 그래프 저장
- --serve : 웹 배포 설정
--serve는 개발 시 otp의 작동을 확인하기 위한 옵션으로 개발이 끝난 후에는 입력안하셔도 좋습니다.
java -Xmx16G -jar otp-2.4.0-shaded.jar --port 9090 --load --serve.
이건 load 커맨드입니다. 최초 빌드 시 graph.obj파일이 생성되면 위에서 말했던 교통 네트워크 그래프 파일입니다. 해당 파일을 이용해 otp에서 경로 탐색을 시도하기 때문에 파일이 생성되기때문에 이후에는 load 커맨드를 입력해주시면 좋습니다.
개발하다보면 build, load를 여러번 하기 때문에 어디 메모장에 입력해서 복사 붙여넣기 하게됩니다. 하지만 매번 이렇게 실행하기는 번거롭기 때문에 스크립트 파일을 하나 만들어 더 간편하게 실행해봅시다! 먼저 [이름].sh 파일을 만든 후 다음 스크립트를 입력해주세요
#!/bin/bash
# OTP 빌드 스크립트
build_otp() {
java -Xmx16G -jar otp-2.3.0-shaded.jar --port 9090 --build --save --serve .
}
# OTP 로드 스크립트
load_otp() {
java -Xmx16G -jar otp-2.3.0-shaded.jar --port 9090 --load --serve .
}
# 명령어에 따라 실행
case "$1" in
"build")
build_otp
;;
"load")
load_otp
;;
*)
echo "사용법: $0 [build|load]"
exit 1
;;
esac
1번째 줄도 생략하지말고 넣어주세요. 쉘 스크립트의 셔뱅 지시어로 스크립트를 실행할 때 어떤 인터프리터를 사용해야하는디 알려주는 역할입니다. 내용은 case문을 사용해 `build, load, 기타 입력 값`을 입력했을 때 분기처리를 해주는 내용입니다.
저는 파일이름을 start.sh로 했고 이제 파일에 실행 권한을 줘야합니다.
chmod +x start.sh
실행 권한까지 줬으니 편하게 실행해주면 됩니다.
./start.sh build
./start.sh load
둘 중 하나를 입력해 경로 탐색 엔진을 사용해주시면 됩니다!
--serve 옵션을 추가했다면 localhost:[설정 포트]로 접속해 osm 데이터를 사용한 otp의 작동을 확인할 수 있습니다.
웹에서 특정 위치의 좌표를 확인할 수 있으나 현재 알 수 없는 오류로 웹에서의 경로 탐색을 시각화는 안됩니다. 하지만 otp에 직접 요청을 보내면 성공적으로 경로 탐색 내용을 확인할 수 있습니다.
http://localhost:9090/otp/routers/default/plan?fromPlace=37.56499,%20126.97950&toPlace=37.56571,%20126.98613
3. Spring 연동
이제 Http요청을 통해 otp에 요청을 보내봅시다.
fun findRoute(start: String, arrive: String) {
val response = RestClient.builder()
.baseUrl("http://localhost:9090/otp/routers/default/plan")
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.defaultHeader(HttpHeaders.ACCEPT_CHARSET, "UTF-8")
.build()
.get()
.uri {
it.queryParam("fromPlace", start)
.queryParam("toPlace", arrive)
.queryParam("arriveBy", false)
.queryParam("mode", "TRANSIT,WALK")
.queryParam("date", LocalDate.now())
.queryParam("time", URLEncoder.encode("7:30am", StandardCharsets.UTF_8))
.build()
}
.retrieve()
.body(String::class.java)
println(response)
}
저는 Http요청을 보내는 방법 중 RestTemplate, WebClient, RestClient가 있습니다. 저는 저번에 작성한 코드가 RestClient로 작성되어있어 편의상 RestClient를 사용했습니다. Http 요청을 보내는것이기 때문에 다른 방법을 사용해도 무관합니다.
- fromPlace : 출발지
- toPlace: 도착지
- mode : 이동 수단(TRANSIT, WALK, BIKE, CAR...)
- date: 출발 날짜
- time : 출발 시각
제 프로젝트는 출퇴근 시간을 고려하고 저는 퇴근보다 출근을 더 중요하게 생각하기 때문에 예상 출근 시간을 입력했습니다. date, time을 입력안하시면 현재 날짜, 시간에 해당하는 데이터를 경로 탐색을 진행합니다.
이제 spring을 실행 후 설정한 경로에 요청을 보내면
4. 오차
성복역 - 영통역
경기도청 - 안양시청
성복역 - 논현역
죽전역 - 광화문
약 10분 정도의 오차를 보이며 저는 많은 양의 부동산 매물을 거리에 따라 탐색, 정렬 후 반환할 예정이기 때문에 이 정도면 충분히 사용 가능할 것 같습니다 :)