Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 145 additions & 101 deletions src/main/java/com/fitlink/service/TmapRouteService.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ public class TmapRouteService {

private static final String BASE_URL = "https://apis.openapi.sk.com";

/* ---------------------------
* 1) 도보 경로
* --------------------------- */

/* =================================================
* WALK
* ================================================= */
public RouteResponseDTO getPedestrianRoute(float oLat, float oLng, float dLat, float dLng) {

String url = BASE_URL + "/tmap/routes/pedestrian?version=1";
Expand All @@ -38,19 +39,19 @@ public RouteResponseDTO getPedestrianRoute(float oLat, float oLng, float dLat, f
"endY": %f,
"startName": "출발지",
"endName": "도착지",
"searchOption": "0",
"reqCoordType": "WGS84GEO",
"resCoordType": "WGS84GEO"
}
""", oLng, oLat, dLng, dLat);

TmapRouteDTO dto = post(url, body);
return convertWalkCar("walk", dto);
TmapRouteDTO dto = post(url, body, TmapRouteDTO.class);
return convertWalk(dto);
}

/* ---------------------------
* 2) 자동차 경로
* --------------------------- */

/* =================================================
* CAR
* ================================================= */
public RouteResponseDTO getCarRoute(float oLat, float oLng, float dLat, float dLng) {

String url = BASE_URL + "/tmap/routes?version=1";
Expand All @@ -63,19 +64,19 @@ public RouteResponseDTO getCarRoute(float oLat, float oLng, float dLat, float dL
"endY": %f,
"startName": "출발지",
"endName": "도착지",
"searchOption": "0",
"reqCoordType": "WGS84GEO",
"resCoordType": "WGS84GEO"
}
""", oLng, oLat, dLng, dLat);

TmapRouteDTO dto = post(url, body);
return convertWalkCar("car", dto);
TmapCarRouteDTO dto = post(url, body, TmapCarRouteDTO.class);
return convertCar(dto);
}

/* ---------------------------
* 3) 대중교통 경로
* --------------------------- */

/* =================================================
* TRANSIT
* ================================================= */
public RouteResponseDTO getTransitRoute(float oLat, float oLng, float dLat, float dLng) {

String url = UriComponentsBuilder.fromHttpUrl(BASE_URL + "/transit/routes")
Expand All @@ -86,136 +87,179 @@ public RouteResponseDTO getTransitRoute(float oLat, float oLng, float dLat, floa
.queryParam("endY", dLat)
.queryParam("reqCoordType", "WGS84GEO")
.queryParam("resCoordType", "WGS84GEO")
.queryParam("sort", 0)
.queryParam("lang", 0)
.queryParam("format", "json")
.toUriString();

HttpHeaders headers = new HttpHeaders();
headers.set("appKey", appKey);
ResponseEntity<TmapRouteDTO> res =
restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(buildHeaders()), TmapRouteDTO.class);

ResponseEntity<TransitRouteDTO> response =
restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(headers), TransitRouteDTO.class);

return convertTransit(response.getBody());
return RouteResponseDTO.builder()
.type("transit")
.duration(0)
.path(new ArrayList<>())
.waypoints(new ArrayList<>())
.build();
}

/* ---------------------------
* POST 공통
* --------------------------- */
private TmapRouteDTO post(String url, String body) {

HttpHeaders headers = new HttpHeaders();
headers.set("appKey", appKey);
headers.setContentType(MediaType.APPLICATION_JSON);
/* =================================================
* POST 공용
* ================================================= */
private <T> T post(String url, String body, Class<T> clazz) {

HttpEntity<String> entity = new HttpEntity<>(body, headers);
HttpEntity<String> entity = new HttpEntity<>(body, buildHeaders());

ResponseEntity<String> rawResponse =
ResponseEntity<String> raw =
restTemplate.exchange(url, HttpMethod.POST, entity, String.class);

System.out.println("[Tmap Raw Response] = " + rawResponse.getBody());
System.out.println("[RAW] = " + raw.getBody());

try {
return new ObjectMapper().readValue(rawResponse.getBody(), TmapRouteDTO.class);

return new ObjectMapper().readValue(raw.getBody(), clazz);
} catch (Exception e) {
System.err.println("DTO 변환 오류: " + e.getMessage());
System.err.println(clazz.getSimpleName() + " 변환 오류: " + e.getMessage());
return null;
}
}

/* ---------------------------
* Walk / Car 변환
* --------------------------- */
private RouteResponseDTO convertWalkCar(String type, TmapRouteDTO dto) {

if (dto == null || dto.getFeatures() == null || dto.getFeatures().isEmpty()) {
return RouteResponseDTO.builder()
.type(type)
.distance(0)
.duration(0)
.path(new ArrayList<>())
.waypoints(new ArrayList<>())
.build();
}
private HttpHeaders buildHeaders() {
HttpHeaders h = new HttpHeaders();
h.set("appKey", appKey);
h.setContentType(MediaType.APPLICATION_JSON);
return h;
}


int distance = dto.getFeatures().get(0).getProperties().getTotalDistance();
int seconds = dto.getFeatures().get(0).getProperties().getTotalTime();
int minutes = seconds / 60;
/* =================================================
* WALK 변환
* ================================================= */
private RouteResponseDTO convertWalk(TmapRouteDTO dto) {

if (dto == null || dto.getFeatures() == null) return empty("walk");

int dist = dto.getFeatures().get(0).getProperties().getTotalDistance();
int time = dto.getFeatures().get(0).getProperties().getTotalTime() / 60;

List<List<Double>> path = new ArrayList<>();
List<RouteResponseDTO.Waypoint> waypoints = new ArrayList<>();

dto.getFeatures().forEach(feature -> {
for (var f : dto.getFeatures()) {
var geo = f.getGeometry();
var prop = f.getProperties();
if (geo == null) continue;

var geo = feature.getGeometry();
var prop = feature.getProperties();
if (geo.getPoint() != null) {
double lat = geo.getPoint().get(1);
double lng = geo.getPoint().get(0);
path.add(List.of(lat, lng));
}

if (geo == null || geo.getType() == null) return;
if (geo.getLine() != null) {
geo.getLine().forEach(c -> path.add(List.of(c.get(1), c.get(0))));
}

switch (geo.getType()) {
if (prop != null && prop.getDescription() != null && prop.getTurnType() != null) {
waypoints.add(new RouteResponseDTO.Waypoint(
path.get(path.size()-1).get(0),
path.get(path.size()-1).get(1),
prop.getDescription()
));
}
}

case "LineString" -> {
if (geo.getLine() != null)
geo.getLine().forEach(coord ->
path.add(List.of(coord.get(1), coord.get(0))));
}
return RouteResponseDTO.builder()
.type("walk")
.distance(dist)
.duration(time)
.path(path)
.waypoints(waypoints)
.build();
}

case "Point" -> {
if (geo.getPoint() != null) {

var c = geo.getPoint();
double lat = c.get(1);
double lng = c.get(0);
/* =================================================
* CAR 변환
* ================================================= */
private RouteResponseDTO convertCar(TmapCarRouteDTO dto) {

path.add(List.of(lat, lng));
if (dto == null || dto.getFeatures() == null) {
return empty("car");
}

int[] totalDistance = {0};
int[] totalTime = {0};

List<List<Double>> path = new ArrayList<>();
List<RouteResponseDTO.Waypoint> waypoints = new ArrayList<>();

// ★ turnType이 있으면 경유지로 추가
if (prop != null && prop.getTurnType() != null) {
dto.getFeatures().forEach(f -> {

waypoints.add(
new RouteResponseDTO.Waypoint(
lat,
lng,
prop.getDescription()
)
);
}
}
}
var geo = f.getGeometry();
var prop = f.getProperties();

case "MultiLineString" -> {
if (geo.getMulti() != null)
geo.getMulti().forEach(line ->
line.forEach(coord ->
path.add(List.of(coord.get(1), coord.get(0)))));
}
// 거리/시간 누적
if (prop != null) {
totalDistance[0] += prop.getDistanceInt();
totalTime[0] += prop.getTimeInt();
}

if (geo == null) return;

/* ------------------------
* MULTILINESTRING
* ------------------------ */
if ("MultiLineString".equals(geo.getType()) && geo.getMulti() != null) {
geo.getMulti().forEach(line ->
line.forEach(coord ->
path.add(List.of(coord.get(1), coord.get(0))))
);
}

/* ------------------------
* LINESTRING
* ------------------------ */
if ("LineString".equals(geo.getType()) && geo.getLine() != null) {
geo.getLine().forEach(coord ->
path.add(List.of(coord.get(1), coord.get(0))));
}

/* ------------------------
* POINT + name 기반 waypoint
* ------------------------ */
if ("Point".equals(geo.getType()) &&
geo.getPoint() != null &&
prop != null &&
(prop.getDescription() != null || prop.getIndex() != null)) {

double lat = geo.getPoint().get(1);
double lng = geo.getPoint().get(0);

String desc = prop.getDescription() != null
? prop.getDescription()
: ("지점_" + prop.getIndex());

waypoints.add(new RouteResponseDTO.Waypoint(lat, lng, desc));
}

});

return RouteResponseDTO.builder()
.type(type)
.distance(distance)
.duration(minutes)
.type("car")
.distance(totalDistance[0])
.duration(totalTime[0] / 60)
.path(path)
.waypoints(waypoints)
.build();
}

/* ---------------------------
* Transit 변환
* --------------------------- */
private RouteResponseDTO convertTransit(TransitRouteDTO dto) {

TransitRouteDTO.Itinerary it =
dto.getMetaData().getPlan().getItineraries().get(0);

int totalDuration = it.getDuration() / 60;

private RouteResponseDTO empty(String type) {
return RouteResponseDTO.builder()
.type("transit")
.duration(totalDuration)
.type(type)
.distance(0)
.duration(0)
.path(new ArrayList<>())
.waypoints(new ArrayList<>())
.build();
}
}
23 changes: 12 additions & 11 deletions src/main/java/com/fitlink/web/dto/RouteResponseDTO.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
package com.fitlink.web.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.*;

import java.util.List;

@Data
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class RouteResponseDTO {

private String type; // walk / car / transit
private int duration; // minutes
private int distance; // meters (walk/car only)
private String type;
private int distance;
private int duration;
private List<List<Double>> path;
private List<Waypoint> waypoints;

private List<Waypoint> waypoints; // ★ 자동 추출된 안내 포인트

@Data
@Getter
@Setter
@AllArgsConstructor
public static class Waypoint {
private double lat;
private double lng;
private String description; // 예: "좌회전", "횡단보도 건너기"
private String description;
}
}
Loading