diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/error/ErrorCode.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/error/ErrorCode.java index d28b618..f97e66d 100644 --- a/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/error/ErrorCode.java +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/error/ErrorCode.java @@ -12,6 +12,10 @@ public enum ErrorCode { FASTEST_ROUTE_NOT_FOUND(422, "경로가 없습니다."), SAME_START_AND_END_POINT(400, "출발지와 도착지가 같습니다."), + // 루트 + ROUTE_NOT_FOUND(404, "루트를 찾을 수 없습니다."), + CAUTION_DANGER_CANT_EXIST_SIMULTANEOUSLY(400, "위험요소와 주의요소는 동시에 존재할 수 없습니다."), + // 건물 노드 BUILDING_NOT_FOUND(404, "유효한 건물을 찾을 수 없습니다."), ; diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/exception/custom/DangerCautionConflictException.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/exception/custom/DangerCautionConflictException.java new file mode 100644 index 0000000..c2bd504 --- /dev/null +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/exception/custom/DangerCautionConflictException.java @@ -0,0 +1,10 @@ +package com.softeer5.uniro_backend.common.exception.custom; + +import com.softeer5.uniro_backend.common.error.ErrorCode; +import com.softeer5.uniro_backend.common.exception.CustomException; + +public class DangerCautionConflictException extends CustomException { + public DangerCautionConflictException(String message, ErrorCode errorCode) { + super(message, errorCode); + } +} diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/exception/custom/RouteNotFoundException.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/exception/custom/RouteNotFoundException.java new file mode 100644 index 0000000..a7efd57 --- /dev/null +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/exception/custom/RouteNotFoundException.java @@ -0,0 +1,10 @@ +package com.softeer5.uniro_backend.common.exception.custom; + +import com.softeer5.uniro_backend.common.error.ErrorCode; +import com.softeer5.uniro_backend.common.exception.CustomException; + +public class RouteNotFoundException extends CustomException { + public RouteNotFoundException(String message, ErrorCode errorCode) { + super(message, errorCode); + } +} diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/utils/Utils.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/utils/Utils.java new file mode 100644 index 0000000..17be94d --- /dev/null +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/common/utils/Utils.java @@ -0,0 +1,45 @@ +package com.softeer5.uniro_backend.common.utils; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.io.WKTWriter; + +import java.util.List; + +public final class Utils { + private static final GeometryFactory geometryFactory = new GeometryFactory(); + + private Utils(){ + // 인스턴스화 방지 + } + + public static Point convertDoubleToPoint(double lat, double lng) { + return geometryFactory.createPoint(new Coordinate(lat, lng)); + } + + public static String convertDoubleToPointWTK(double lat, double lng) { + return String.format("POINT(%f %f)", lat, lng); + } + + public static LineString convertDoubleToLineString(List co){ + if(co==null || co.isEmpty()){ + throw new IllegalArgumentException("coordinates can not be null or empty"); + } + + Coordinate[] coordinates = new Coordinate[co.size()]; + for (int i = 0; i < co.size(); i++) { + coordinates[i] = new Coordinate(co.get(i)[0], co.get(i)[1]); + } + + return geometryFactory.createLineString(coordinates); + } + + public static String convertDoubleToLineStringWTK(List co){ + LineString lineString = convertDoubleToLineString(co); + WKTWriter writer = new WKTWriter(); + return writer.write(lineString); + } + +} diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/controller/RouteApi.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/controller/RouteApi.java index 11750c6..7e370c5 100644 --- a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/controller/RouteApi.java +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/controller/RouteApi.java @@ -1,17 +1,15 @@ package com.softeer5.uniro_backend.route.controller; -import com.softeer5.uniro_backend.route.dto.FastestRouteResDTO; -import com.softeer5.uniro_backend.route.dto.GetAllRoutesResDTO; +import com.softeer5.uniro_backend.route.dto.*; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; -import com.softeer5.uniro_backend.route.dto.GetRiskRoutesResDTO; - import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @@ -33,6 +31,26 @@ public interface RouteApi { }) ResponseEntity getRiskRoutes(@PathVariable("univId") Long univId); + @Operation(summary = "단일 route의 위험&주의 요소 조회") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "단일 route의 위험&주의 요소 조회 성공"), + @ApiResponse(responseCode = "400", description = "EXCEPTION(임시)", content = @Content), + }) + ResponseEntity getRisk(@PathVariable("univId") Long univId, + @RequestParam(value = "start-lat") double startLat, + @RequestParam(value = "start-lng") double startLng, + @RequestParam(value = "end-lat") double endLat, + @RequestParam(value = "end-lng") double endLng); + + @Operation(summary = "위험&주의 요소 제보") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "단일 route의 위험&주의 요소 제보 성공"), + @ApiResponse(responseCode = "400", description = "EXCEPTION(임시)", content = @Content), + }) + ResponseEntity updateRisk(@PathVariable("univId") Long univId, + @PathVariable("routeId") Long routeId, + @RequestBody PostRiskReqDTO postRiskReqDTO); + @Operation(summary = "빠른 길 계산") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "빠른 길 계산 성공"), diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/controller/RouteController.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/controller/RouteController.java index 07644fa..94fbdab 100644 --- a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/controller/RouteController.java +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/controller/RouteController.java @@ -1,15 +1,10 @@ package com.softeer5.uniro_backend.route.controller; -import com.softeer5.uniro_backend.route.dto.FastestRouteResDTO; -import com.softeer5.uniro_backend.route.dto.GetAllRoutesResDTO; +import com.softeer5.uniro_backend.route.dto.*; import com.softeer5.uniro_backend.route.service.RouteCalculationService; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; -import com.softeer5.uniro_backend.route.dto.GetRiskRoutesResDTO; import com.softeer5.uniro_backend.route.service.RouteService; import lombok.RequiredArgsConstructor; @@ -26,7 +21,7 @@ public class RouteController implements RouteApi { @Override @GetMapping("/{univId}/routes") public ResponseEntity> getAllRoutesAndNodes(@PathVariable("univId") Long univId){ - List allRoutes = routeService.GetAllRoutes(univId); + List allRoutes = routeService.getAllRoutes(univId); return ResponseEntity.ok().body(allRoutes); } @@ -37,11 +32,31 @@ public ResponseEntity getRiskRoutes(@PathVariable("univId") return ResponseEntity.ok().body(riskRoutes); } + @Override + @GetMapping("/{univId}/route/risk") + public ResponseEntity getRisk(@PathVariable("univId") Long univId, + @RequestParam(value = "start-lat") double startLat, + @RequestParam(value = "start-lng") double startLng, + @RequestParam(value = "end-lat") double endLat, + @RequestParam(value = "end-lng") double endLng){ + GetRiskResDTO riskResDTO = routeService.getRisk(univId,startLat,startLng,endLat,endLng); + return ResponseEntity.ok().body(riskResDTO); + } + + @Override + @PostMapping("/{univId}/route/risk/{routeId}") + public ResponseEntity updateRisk (@PathVariable("univId") Long univId, + @PathVariable("routeId") Long routeId, + @RequestBody PostRiskReqDTO postRiskReqDTO){ + routeService.updateRisk(univId,routeId,postRiskReqDTO); + return ResponseEntity.ok().build(); + } + @Override @GetMapping("/{univId}/routes/fastest") public ResponseEntity calculateFastestRoute(@PathVariable("univId") Long univId, - @RequestParam Long startNodeId, - @RequestParam Long endNodeId) { + @RequestParam(value = "start-node-id") Long startNodeId, + @RequestParam(value = "end-node-id") Long endNodeId) { FastestRouteResDTO fastestRouteResDTO = routeCalculationService.calculateFastestRoute(univId, startNodeId, endNodeId); return ResponseEntity.ok(fastestRouteResDTO); } diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/dto/GetRiskResDTO.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/dto/GetRiskResDTO.java new file mode 100644 index 0000000..5f188d5 --- /dev/null +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/dto/GetRiskResDTO.java @@ -0,0 +1,28 @@ +package com.softeer5.uniro_backend.route.dto; + +import com.softeer5.uniro_backend.route.entity.CautionType; +import com.softeer5.uniro_backend.route.entity.DangerType; +import com.softeer5.uniro_backend.route.entity.Route; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Getter +@Schema(name = "GetAllRoutesResDTO", description = "특정 간선의 주의/위험요소 조회 DTO") +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public class GetRiskResDTO { + @Schema(description = "route ID", example = "3") + private final Long routeId; + @Schema(description = "위험 요소 타입 리스트", example = "[\"SLOPE\", \"STAIRS\"]") + private final List cautionTypes; + @Schema(description = "위험 요소 타입 리스트", example = "[\"SLOPE\", \"STAIRS\"]") + private final List dangerTypes; + + public static GetRiskResDTO of(Route route) { + return new GetRiskResDTO(route.getId(),route.getCautionFactorsByList(), route.getDangerFactorsByList()); + } + +} diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/dto/PostRiskReqDTO.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/dto/PostRiskReqDTO.java new file mode 100644 index 0000000..2ec52d7 --- /dev/null +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/dto/PostRiskReqDTO.java @@ -0,0 +1,15 @@ +package com.softeer5.uniro_backend.route.dto; + +import com.softeer5.uniro_backend.route.entity.CautionType; +import com.softeer5.uniro_backend.route.entity.DangerType; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +public class PostRiskReqDTO { + private List cautionTypes; + private List dangerTypes; +} diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/entity/Route.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/entity/Route.java index 31e6620..bc7e727 100644 --- a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/entity/Route.java +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/entity/Route.java @@ -2,6 +2,7 @@ import static jakarta.persistence.FetchType.*; +import java.util.List; import java.util.Set; import com.softeer5.uniro_backend.resolver.CautionListConverter; @@ -59,4 +60,23 @@ public class Route { @Convert(converter = DangerListConverter.class) @Column(name = "danger_factors") private Set dangerFactors; + + public List getCautionFactorsByList(){ + return cautionFactors.stream().toList(); + } + + public List getDangerFactorsByList(){ + return dangerFactors.stream().toList(); + } + + public void setCautionFactors(List cautionFactors) { + this.cautionFactors.clear(); + this.cautionFactors.addAll(cautionFactors); + } + + public void setDangerFactors(List dangerFactors) { + this.dangerFactors.clear(); + this.dangerFactors.addAll(dangerFactors); + } + } diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/repository/RouteRepository.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/repository/RouteRepository.java index fe442d9..695a373 100644 --- a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/repository/RouteRepository.java +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/repository/RouteRepository.java @@ -1,6 +1,7 @@ package com.softeer5.uniro_backend.route.repository; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; @@ -26,4 +27,38 @@ public interface RouteRepository extends JpaRepository { ) List findRiskRouteByUnivIdWithNode(@Param("univId") Long univId); + @Query(value = """ + SELECT r.* FROM route r + JOIN node n1 ON r.node1_id = n1.id + JOIN node n2 ON r.node2_id = n2.id + WHERE r.univ_id = :univId + AND ( + (n1.coordinates = ST_SRID(ST_GeomFromText(:point1), 4326) + AND n2.coordinates = ST_SRID(ST_GeomFromText(:point2), 4326)) + OR + (n1.coordinates = ST_SRID(ST_GeomFromText(:point2), 4326) + AND n2.coordinates = ST_SRID(ST_GeomFromText(:point1), 4326)) + ) + """, nativeQuery = true) + Optional findRouteByPointsAndUnivId( + @Param("univId") Long univId, + @Param("point1") String point1, + @Param("point2") String point2 + ); + + @Query(value = """ + SELECT r.* FROM route r + WHERE r.univ_id = :univId + AND ( + r.path = ST_SRID(ST_GeomFromText(:path), 4326) + OR + r.path = ST_SRID(ST_GeomFromText(:rev_path), 4326) + ) +""", nativeQuery = true) + Optional findRouteByLineStringAndUnivId(@Param("univId") Long univId, + @Param("path")String path, + @Param("rev_path")String revPath); + + Optional findByIdAndUnivId (Long id, Long univId); + } diff --git a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/service/RouteService.java b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/service/RouteService.java index c856a30..6c8ae2c 100644 --- a/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/service/RouteService.java +++ b/uniro_backend/src/main/java/com/softeer5/uniro_backend/route/service/RouteService.java @@ -2,15 +2,16 @@ import java.util.List; -import com.softeer5.uniro_backend.route.dto.GetAllRoutesResDTO; +import com.softeer5.uniro_backend.common.error.ErrorCode; +import com.softeer5.uniro_backend.common.exception.custom.DangerCautionConflictException; +import com.softeer5.uniro_backend.common.exception.custom.RouteNotFoundException; +import com.softeer5.uniro_backend.common.utils.Utils; +import com.softeer5.uniro_backend.route.dto.*; import com.softeer5.uniro_backend.route.entity.CoreRoute; import com.softeer5.uniro_backend.route.repository.CoreRouteRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import com.softeer5.uniro_backend.route.dto.GetCautionResDTO; -import com.softeer5.uniro_backend.route.dto.GetDangerResDTO; -import com.softeer5.uniro_backend.route.dto.GetRiskRoutesResDTO; import com.softeer5.uniro_backend.route.entity.Route; import com.softeer5.uniro_backend.route.repository.RouteRepository; @@ -24,7 +25,7 @@ public class RouteService { private final CoreRouteRepository coreRouteRepository; - public List GetAllRoutes(Long univId) { + public List getAllRoutes(Long univId) { List coreRoutes = coreRouteRepository.findByUnivId(univId); return coreRoutes.stream().map(GetAllRoutesResDTO::of).toList(); } @@ -45,7 +46,7 @@ private List mapRoutesToDangerDTO(List routes) { route.getNode1(), route.getNode2(), route.getId(), - route.getDangerFactors().stream().toList() + route.getDangerFactorsByList() )).toList(); } @@ -56,7 +57,48 @@ private List mapRoutesToCautionDTO(List routes) { route.getNode1(), route.getNode2(), route.getId(), - route.getCautionFactors().stream().toList() + route.getCautionFactorsByList() )).toList(); } + + + public GetRiskResDTO getRisk(Long univId, double startLat, double startLng, double endLat, double endLng) { + String startWTK = Utils.convertDoubleToPointWTK(startLat, startLng); + String endWTK = Utils.convertDoubleToPointWTK(endLat, endLng); + + Route routeWithJoin = routeRepository.findRouteByPointsAndUnivId(univId, startWTK ,endWTK) + .orElseThrow(() -> new RouteNotFoundException("Route Not Found", ErrorCode.ROUTE_NOT_FOUND)); + + /* + // LineString 사용버전 + List coordinates = Arrays.asList( + new double[]{startLat, startLng}, + new double[]{endLat, endLng} + ); + String lineStringWTK = Utils.convertDoubleToLineStringWTK(coordinates); + Collections.reverse(coordinates); + String reverseLineStringWTK = Utils.convertDoubleToLineStringWTK(coordinates); + + Route routeWithoutJoin = routeRepository.findRouteByLineStringAndUnivId(univId,lineStringWTK,reverseLineStringWTK) + .orElseThrow(() -> new RouteNotFoundException("Route Not Found", ErrorCode.ROUTE_NOT_FOUND)); + + */ + + + return GetRiskResDTO.of(routeWithJoin); + } + + @Transactional + public void updateRisk(Long univId, Long routeId, PostRiskReqDTO postRiskReqDTO) { + Route route = routeRepository.findByIdAndUnivId(routeId, univId) + .orElseThrow(() -> new RouteNotFoundException("Route not Found", ErrorCode.ROUTE_NOT_FOUND)); + + if(!postRiskReqDTO.getCautionTypes().isEmpty() && !postRiskReqDTO.getDangerTypes().isEmpty()){ + throw new DangerCautionConflictException("DangerFactors and CautionFactors can't exist simultaneously.", + ErrorCode.CAUTION_DANGER_CANT_EXIST_SIMULTANEOUSLY); + } + + route.setCautionFactors(postRiskReqDTO.getCautionTypes()); + route.setDangerFactors(postRiskReqDTO.getDangerTypes()); + } }