카풀 서비스에서 나와 출발지와 도착지가 비슷한 게시글들을 검색하는 기능이 있으면 편리할 것이다.

 

따라서 yata 프로젝트에서도 공간데이터를 이용하여, 출발지, 도착지를 입력하면 반경 내 위치의 게시글을 검색해 올 수 있는 API를 구현하기로 하였다.

 

먼저 하버사인 공식을 이용해 두 지점 사이의 최단거리를 계산하고, 그 최단 거리 반경의 minimum bounding polygon을 생성하여 바운더리 내에 있는 게시글을 가져올 수 있도록 하였다.

 

 

해당 기능을 만들기 위해서는 위도,경도,방위각,라디안값에 대한 개념지식이 필요하다.

 

위도
적도를 기준으로 북/남쪽으로 얼마나 떨어져있는 지 나타내는 위치
적도가 0°가 되며, 북쪽으로 올라가면 북위 90°, 남쪽으로 내려가면 남위 90°

경도
그리니치 천문대를 기준으로 동과 서를 세로로 표시
그리니치 천문대에서 오른쪽은 동경, 왼쪽은 서경
180°E(동경)~ 180°W(서경)
라디안
육십분법으로 표현하던 도(°) 단위를 호도법을 이용해 π로 나타낸 것
라디안 사용시 삼각 함수의 형태를 단순하게 표현할 수 있따.

호의길이 = r * rad

2πr(중심각이 360일때 호의 길이 = 원의 지름) = r* rad
360° 는 2π라디안, 180° 는 π라디안임 , 따라서 1° = π/180

특수각의 라디안
육십분법 360° 180° 90° 60° 45° 30°
호도법 π π/2 π/3 π/4 π/6

참고자료
https://www.youtube.com/watch?v=GS5-P_R1Hc8
⭐방위각

진북(북극성,12시방향)을 기준으로 하여 우측으로 잰 각
세타값 공식 = tan^-1((y2-y1)/(x2-x1))

자세한건
방위각 정리 강의

 

 

 

이제 해당 기능을 직접 구현해보자

 

📌설정

 

하이버네이트가 공간 데이터를 다루기 위한 라이브러리 두 가지를 추가

이때 hibernate 버전이 동일하도록 추가한다.

application.yml파일 설정

org.hibernate.spatial.dialect.mysql.MySQL56InnoDBSpatialDialect

=> MySAL 5.6 이상 버전과 InnoDM스토리지 엔진을 사용하는 MYSQL 데이터베이스의 공간 확장 기능을 지원. 이 기능을 사용하면 Hibernate가 MySQL의 공간 데이터 유형(Geometry,Point,Polygon 등)과 공간 함수(ST_*,MBR 등)을 사용할 수 있게 된다. 즉 Hibernate를 통해 공간 데이터를 쿼리하고 처리할 수 있게 되는 것이다.

 

 

 

jts는 공간 관련 기능을 제공하는 Java의 라이브러리이다. hibernate-spatial 라이브러리를 추가하면 import할 수 있다.

 

import org.locationtech.jts.geom.*;

 

 

 

 

 

📌LocationEntity

Entity에 위치정보 컬럼을 추가해준다

Point 타입 사용 시 org.locationtech.jts.geom.Point 패키지를 사용

Location Entity에 포인트 타입 필드를 추가해주었다

이때, 위와 같이 geography의 타입을 columnDefinition = "GEOMETRY"으로 설정하여 MySql에 저장시 MySql의 GEOMETRY 타입으로 저장될 수 있도록 한다.

 

 

 

 

⭐기준 좌표에서 일정 거리 떨어진 좌표 데이터 구하기

 

이론- 하버사인 공식(Haversine Formula)을 이용히여 구의 두 지점 사이의 최단거리를 구한다.

 

실제 지구는 적도 쪽이 좀 더 길쭉한 타원형이기 때문에 완벽히 정확한 값이라고 할 수는 없지만,

일반적인 사용에서는 큰 무리는 없을 정도임

 

지도 API에서 거리 계산을 해주는 함수가 있지만 좌표값만으로 직접 구해야 할 일이 있을 경우 하버사인 공식을 이용해야 한다! 따라서 우리 프로젝트에서도 하버사인 공식을 이용하여 거리를 계산해 주었다 

 

최단거리 구하기-하버사인 공식

 

 

📌LocationDto

 

 

📌GeometricUtils

 

계산을 위한 Utility클래스,static으로 사용한다.

 

public class GeometryUtils {
    public static LocationDto.Post locationToDto(Point point, double distance, Double bearing) {
        return calculateByDirection(point.getX(), point.getY(), distance, bearing);
    }

    public static Geometry createPoint(double lat, double lng) throws ParseException {
        return new WKTReader().read("POINT(" + lng + " " + lat + ")");
    }


    public static LocationDto.Post calculateByDirection(Double baseLatitude, Double baseLongitude, double distance,
                                                        Double bearing) {

        Double radianLatitude = toRadian(baseLatitude);
        Double radianLongitude = toRadian(baseLongitude);
        Double radianAngle = toRadian(bearing);
        Double distanceRadius = distance / 6371.01;

        Double latitude = Math.asin(sin(radianLatitude) * cos(distanceRadius) +
                cos(radianLatitude) * sin(distanceRadius) * cos(radianAngle));
        Double longitude = radianLongitude + Math.atan2(sin(radianAngle) * sin(distanceRadius) * cos(radianLatitude),
                cos(distanceRadius) - sin(radianLatitude) * sin(latitude));

        longitude = (longitude + 540) % 360 - 180;

        return LocationDto.Post.of(toDegree(longitude), toDegree(latitude), null);
    }

    private static Double toRadian(Double coordinate) {
        return coordinate * Math.PI / 180.0;
    }

    private static Double toDegree(Double coordinate) {
        return coordinate * 180.0 / Math.PI;
    }

    private static Double sin(Double coordinate) {
        return Math.sin(coordinate);
    }

    private static Double cos(Double coordinate) {
        return Math.cos(coordinate);
    }

    public static Point getEmptyPoint() throws ParseException {
        Double latitude = 0.0;
        Double longitude = 0.0;
        String pointWKT = String.format("POINT(%s %s)", longitude, latitude);
        return (Point) new WKTReader().read(pointWKT);
    }

    public static Point getPoint(LocationDto.Post location) {
        double latitude = location.getLatitude();
        double longitude = location.getLongitude();
        GeometryFactory gf = new GeometryFactory();
        return gf.createPoint(new Coordinate(latitude, longitude));
    }

    public static Geometry getMBR(Geometry startGeometry, double distance) {
        return startGeometry.buffer(distance);
    }
}

위 코드에 있는 함수를 하나하나씩 살펴보자!

 

 

 

 

📌locationToDto()

    public static LocationDto.Post locationToDto(Point point, double distance, Double bearing) {
        return calculateByDirection(point.getX(), point.getY(), distance, bearing);
    }

함수설명

주어진 Point객체의 경도와 위도 값을 추출하여 calculateByDirection()함수를 호출하고, 그 결과로 계산된 위치 정보를 LocatinoDto.Post 객체로 변환하여 반환한다.

 

동작방식

Point객체와 거리(distance) 그리고 방위(bearing)값을 매개변수로 받아 calculateByDirection함수로 넘겨준다

 

point.getx()
point객체의 X좌표인 경도 값을 반환

point.gety() 
point객체의 Y 좌표인 위도 값을 반환 

 

calculateByDirection함수는 주어진 기준 위치의 위도와 경도, 기준 위치에서의 거리,기준 위치에서의 방위 값을 받아
해당 지점의 위치를 계산하고 LocatDto.Post객체를 반환

 

 

 

 

📌createPoint()

    public static Geometry createPoint(double lat, double lng) throws ParseException {
        return new WKTReader().read("POINT(" + lng + " " + lat + ")");
    }

함수설명

위도(lat) 와 경도(lng) 값을 사용하여  WKT(Point) 형식으로 정의된 지점(Geometry)객체를 생성하고 반환

 

동작과정

WKTReader()로 WTK 객체를 생성

WKT Reader는 Well-Known Text(WKT)형식으로 정의된 공간 데이터를 읽고 파싱하는 역할을 수행

 

 

WKT(Well-Known Text)
공간 데이터를 표현하기 위한 텍스트 형식
지리정보 시스템(GIS)에서 사용되며 지점(Point),선(Line),다각형(Polygon) 등과 같은 공간 객체의 위치와 형상을 기술하는 데 사용
<GeometryType>(<Coordinates>)의 형식으로 사용

<GeometryType>
: 공간 객체의 유형 POINT,LINESTRING,POLYGON 등이 사용
<Coordinates>
: 공간 객체의 좌표를 나타내는 부분
괄호로 둘러싸여 있으며, 좌표는 공백이나 쉼표로 구분된 숫자 값으로 표현
ex) 2D 공간에서POINT(10 20)은 x 좌표가 10이고 y 좌표가 20인 지점을 나타냄

WKT는 다양한 공간 객체를 표현할 수 있으며, 좌표의 차원에 따라 2D, 3D, 4D 등 다양한 형식이 지원될 수 있다. 또한 여러 개의 좌표를 포함하는 공간 객체도 표현할 수 있다.
예를 들어, LINESTRING(0 0, 10 10, 20 20)은 세 개의 지점으로 이루어진 선을 나타냄.

이렇게 WTK를 통에 공간 데이터를 읽고 표준화할 수 있으며, WKT를 파싱하여 공간 객체로 변환하거나 반대로 공간 객체를 WKT 형식으로 직렬화할 수도 있다.

 

WKT에 대한 자세한 설명은 요기!

https://locationtech.github.io/jts/javadoc/org/locationtech/jts/io/WKTReader.html

 

 

 

📌ToRadian()

private static Double toRadian(Double coordinate) {
    return coordinate * Math.PI / 180.0;
}

°단위로 표현된 각도를 라디안 단위로 변환

 

Math.PI=> π 값 반환해주는 함수

 

1° = π/180

자세한건 아까 위에 표 참고

 

 

📌toDegree()

    private static Double toDegree(Double coordinate) {
        return coordinate * 180.0 / Math.PI;
    }

라디안 단위로 표현된 좌표를 도 단위로 변환!

 

 

📌sin()

    private static Double sin(Double coordinate) {
        return Math.sin(coordinate);
    }

각도를 받아 Math.sin()함수를 통해 사인값을 계산

 

 

📌cos()

    private static Double cos(Double coordinate) {
        return Math.cos(coordinate);
    }

cos값 계산

 

 

📌calculateByDirection()

    public static LocationDto.Post calculateByDirection(Double baseLatitude, Double baseLongitude, double distance,
                                                        Double bearing) {

        Double radianLatitude = toRadian(baseLatitude);
        Double radianLongitude = toRadian(baseLongitude);
        Double radianAngle = toRadian(bearing);
        Double distanceRadius = distance / 6371.01;

        Double latitude = Math.asin(sin(radianLatitude) * cos(distanceRadius) +
                cos(radianLatitude) * sin(distanceRadius) * cos(radianAngle));
        Double longitude = radianLongitude + Math.atan2(sin(radianAngle) * sin(distanceRadius) * cos(radianLatitude),
                cos(distanceRadius) - sin(radianLatitude) * sin(latitude));

        longitude = (longitude + 540) % 360 - 180;

        return LocationDto.Post.of(toDegree(longitude), toDegree(latitude), null);
    }

 

기준 위치의 위도 경도값, 그리고 기존 위치 값과의 방위각을 받아 새로운 위치를 계산하는 함수이다.

 

먼저 삼각함수 계산을 위해 매개변수로 받아온 값들을 라디안 단위로 변경해 줄 것이다.

위도와 경도, 방위값은 아까 만들어준 라디안 함수를 이용해 라디안 단위로 변경해준다.

거리는 지구 반지름인 6371.01(km)로 나누어 라디안 값으로 변환해준다. (호/r = rad)

 

 

이제 하버사인 공식을 이용해 변경해 준 값으로 새 지점의 위도(φ2), 경도( λ2)값을 구해준다.

Haversine Formula

φ1,φ2  = 지점 1,2의 위도 
λ1,λ2 = 지점 1,2의 경도
θ = 방위각
δ = 지점 1과 지점 2의 거리

 φ2 = asin( sin φ1 ⋅ cos δ + cos φ1 ⋅ sin δ ⋅ cos θ )
 λ2 = λ1 + atan2( sin θ ⋅ sin δ ⋅ cos φ1, cos δ − sin φ1 ⋅ sin φ2 )

 

longitude = (longitude + 540) % 360 - 180;

여기서 경도값에 540을 더하고 360을 나눈 나머지 값에 180을 빼는 이유는

경도값을 -180~ 180의 범위로 조정해 주기 위해서이다.

 

이제 다시 toDgree함수를 이용해 새로 구해준 latitude,longitude값을 도 단위로 변경해준 후 LocationDto.Post객체로 반환한다. 여기서 위,경도값 말고 address 필드는 null로 입력된다.

 

 

 

이제 우리는 GeometryUtil 클래스를 통해

A지점의 위 경도값, 그리고 A지점에서의 방위각, A지점에서의 거리가 주어졌을때 그에 해당하는 B지점을 구할 수 있게 되었다. (●'◡'●)╰(*°▽°*)╯

 

 

 

📌getEmptyPoint()

    public static Point getEmptyPoint() throws ParseException {
        Double latitude = 0.0;
        Double longitude = 0.0;
        String pointWKT = String.format("POINT(%s %s)", longitude, latitude);
        return (Point) new WKTReader().read(pointWKT);
    }

함수설명

위도와 경도값이 0.0인 빈 Point객체를 생성해서 반환

이 함수를 통해 빈 지점을 표현하거나 초기값으로 활용할 수 있다.

 

동작방식

위도와 경도를 0.0으로 초기화 한 후,

String.format("Point(%s,%s)"),longtitude,latitude)를 사용하여 WKT 형식으로 빈 지점을 정의하는 문자열을 생성

여기서 longtitude,latitude값은 문자열 포맷에 맞춰 삽입된다.

 

이제 new WKTReader().read(pointWKT) 를 호출하여 생성된 WKT(Point)문자열을 파싱하여 빈 Point객체를 생성하여 반환한다.

 

 

📌getPoint()

   public static Point getPoint(LocationDto.Post location) {
        double latitude = location.getLatitude();
        double longitude = location.getLongitude();
        GeometryFactory gf = new GeometryFactory();
        return gf.createPoint(new Coordinate(latitude, longitude));
    }

LocatinoDto.Post객체에서 위도와 경도값을 가져와, 해당 좌표를 이용해 Point객체를 생성하여 반환한다.

 

공간객체를 생성하기 위해 Geometry객체를 생성하고,

통해 위도,경도 좌표를 가지고 Point객체를 생성하여 반환한다.

 

 

 

📌getMBR()

    public static Geometry getMBR(Geometry startGeometry, double distance) {
        return startGeometry.buffer(distance);
    }
}

startGeometry와 distance를 받아와 MBR(Mininum Bounding Rectangle)을 생성하여 반환

 

startGeometry.buffer(distance)를 호출하여 해당 Geometry를 기준으로 지정된 거리만큼 버퍼를 생성한다.

버퍼는 시작 지오메트리 주변에 지정된 거리만큼의 영역을 포함하는 도형을 생성하는 역할을 한다.

우리는 나중에 startGeometry에 Point 타입의 출발지, 도착지를 넣어줄 것이다.



대충 함수 설명을 요약하자면, 이 메서드는 주어진 너비를 가진 버퍼 영역을 이 지오메트리 주변에 계산한다.

수학적으로 정확한 버퍼 영역 경계에는 호가 포함될 수 있다.
선형 기하학을 사용해 이런 호를 나타내려면 선분으로 근사화해야 하며,

버퍼의 구조는 사분면당 8개의 선분을 사용하여 구성된다고 하는데 정확한 원리는 모르겠다 ..ㅎ
버퍼의 작업은 항상 다각형의 결과를 반환한다고 한다.

파라미터 : distance(거리) - 버퍼의 넓이(양수/음수/0도 가능)
리턴값 : 버퍼 영역을 나타내는 다각형 지오메트리(빈 경우도 있다.)

 

더 자세한건

https://www.tabnine.com/code/java/methods/org.locationtech.jts.geom.Geometry/buffer

 

 

 

📌Direction Enum

@Getter
public enum Direction {
    NORTH(0.0),
    WEST(270.0),
    SOUTH(180.0),
    EAST(90.0),
    NORTHWEST(315.0),
    SOUTHWEST(225.0),
    SOUTHEAST(135.0),
    NORTHEAST(45.0);

    private final Double bearing;

    Direction(Double bearing) {
        this.bearing = bearing;
    }
}

방위각을 계산하기 위한 Enum 클라스에 동,서,남,북,북서,남서,남동,북동 방향의 방위각을 정의해준다.

 

 

 

이제 야타 검색 기능을 위한 Mapper, Service , Repository, Controller클래스를 구현해보자.

 

 

 

📌yataMapper

default Location postToLocation(LocationDto.Post post) throws ParseException {
    if (post == null) {
        return null;
    }

    Location.LocationBuilder location = Location.builder();

    Point point = GeometryUtils.getPoint(post);

    location.location(point);
    location.address(post.getAddress());


    return location.build();
}

    default LocationDto.Response locationToResponse(Location location) {
        if (location == null) {
            return null;
        }

        LocationDto.Response.ResponseBuilder response = LocationDto.Response.builder();

        response.longitude(location.getLocation().getY());
        response.latitude(location.getLocation().getX());
        response.address(location.getAddress());

        return response.build();
    }
}

 

 

LocationDto.Post 객체를 LocationEntity객체로 변환

(이때 GeometryUtils 클래스의 getPoint() 메서드를 이용하여 Point 객체를 생성한 후 값 주입)

 

LocationEntity 객체를 LocatinoDto.Response객체로 변환

 

 

 

📌YataRepository

    public Slice<Yata> findYataByLocation(Location startLocation, Location endLocation, double distance, YataStatus yataStatus, Pageable pageable) {
        return yataRepository.findYataByStartAndEndLocation(startLocation, endLocation, distance,yataStatus, pageable);
    }
}

이제 드디어 우리가 구현하고 싶었떤 기능인 FindYataByLocation()메서드를 구현할 것이다.

시작 위치와 도착위치, 거리 YataStatus(운전자작성 게시글인지, 탑승자 작성 게시글인지) 를 입력받아 yataReopsitory에서 원하는 게시글을 페이징 처리해서 반환해 줄 것이다.

 

 

 

📌YataRepositoryImpl

📌FindYataStartAndEndLocation()

이제 yataReopsitory에서 만든 메서드를 구현체 클래스에서 구현해준다.

@Override
public Slice<Yata> findYataByStartAndEndLocation(Location startLocation, Location endLocation, double distance,YataStatus yataStatus, Pageable pageable) {

    // QueryDsl
    JPAQuery<Yata> query = queryFactory.selectFrom(yata)
            .join(yata.member).fetchJoin()
            .join(yata.strPoint).fetchJoin()
            .join(yata.destination).fetchJoin()
            .leftJoin(yata.yataMembers, yataMember).fetchJoin()
            .orderBy(yata.yataId.desc())
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize() + 1L);
    // MBR
    if(startLocation.getLocation().getX() != 0 && startLocation.getLocation().getY() != 0) {
        Geometry startMBR = GeometryUtils.getMBR(startLocation.getLocation(), distance / 100);
        query.where(yata.strPoint.location.intersects(startMBR));
    }
    if(endLocation.getLocation().getX() != 0 && endLocation.getLocation().getY() != 0) {
        Geometry endMBR = GeometryUtils.getMBR(endLocation.getLocation(), distance / 100);
        query.where(yata.destination.location.intersects(endMBR));
    }
    if(yataStatus != null){
        query.where(yata.yataStatus.eq(yataStatus));
    }
    return (Slice<Yata>) generateYataSlice(pageable, query);

}

먼저, QuesyDsl을 사용하여 yataEntity 조회를 위한 쿼리를 생성한다.

selectFrom(yata)를 통해 yata Entity를 선택하고, join, fetchjoin을 통해 연관 엔티티와 조인 후, orderBy() 를 통해 최근 게시글 순으로 정렬한 후 offset() 와 limit()를 통해 페이지네이션 처리를 해주었따.

 

    if(startLocation.getLocation().getX() != 0 && startLocation.getLocation().getY() != 0) {
        Geometry startMBR = GeometryUtils.getMBR(startLocation.getLocation(), distance / 100);
        query.where(yata.strPoint.location.intersects(startMBR));
    }
    if(endLocation.getLocation().getX() != 0 && endLocation.getLocation().getY() != 0) {
        Geometry endMBR = GeometryUtils.getMBR(endLocation.getLocation(), distance / 100);
        query.where(yata.destination.location.intersects(endMBR));
    }

아 부분에서 출발위치와 도착위치의 좌표가 0이 아닌 경우 distince를 기반으로 시작 위치를 중심으로 하는 MBR을 생성한다.

이때 아까 만들어준 GeometryUtils클라스의 getMBR 메서드를 이용한다.

 

그리고 생성된 MBR과 Yata의 출발,도착지가 겹치는지를 확인하기 위해 intersects()를 사용하여 조건을 추가한디ㅏ.

 

여기서 intersects() 메서드는 지오메트리 객체의 메서드로서 JTS 라이브러리의 Geometry클래스에 정의된 메서드로, 두 지오메트리의 교차 여부를 판단하는데 활용된다. (교차한다면 true, 그렇지 않다면 false 반환)

 

intersects()에 대해 자세한건 여기

 

 

 

 

📌YataSearchServiceImpl

📌findYataByLocation() 

@Service
public class YataSearchServiceImpl implements YataSearchService {


    private final JpaYataRepository yataRepository;

    public YataSearchServiceImpl(JpaYataRepository yataRepository) {
        this.yataRepository = yataRepository;
    }


    public Slice<Yata> findYataByLocation(Location startLocation, Location endLocation, double distance, YataStatus yataStatus, Pageable pageable) {
        return yataRepository.findYataByStartAndEndLocation(startLocation, endLocation, distance,yataStatus, pageable);
    }
}

yatasearchService 인터페이스를 만들어 준 후 구현체에

findYataByLocation() 메서드를 구현한다.

이때 아까 yataRepository에 구현해 준 findYataByStartAndEndLocation() 을 이용하여 검색된 야타 객체를 모두 가져와 Slice<Yata>형태로 반환하도록 한다.

 

 

 

 

📌yataSearchController

@RestController
@Validated
@RequestMapping("/api/v1/yata/search")
public class YataSearchController {

    private YataSearchService yataSearchService;
    private YataMapper mapper;

    public YataSearchController(YataSearchService yataSearchService, YataMapper mapper) {
        this.yataSearchService = yataSearchService;
        this.mapper = mapper;
    }

    @GetMapping("/location")
    public ResponseEntity getLocation(@RequestParam(required = false) Double startLon,
                                      @RequestParam(required = false) Double startLat,
                                      @RequestParam(required = false) String startAddress,
                                      @RequestParam(required = false) Double endLon,
                                      @RequestParam(required = false) Double endLat,
                                      @RequestParam(required = false) String endAddress,
                                      @RequestParam double distance,
                                      @RequestParam(required = false) String yataStatus,
                                      @PageableDefault Pageable pageable) throws ParseException {
        LocationDto.Post startLocationDto = LocationDto.Post.of(
                startLon == null ? 0 : startLon,
                startLat == null ? 0 : startLat,
                startAddress == null ? "" : startAddress
        );
        LocationDto.Post endLocationDto = LocationDto.Post.of(
                endLon == null ? 0 : endLon,
                endLat == null ? 0 : endLat,
                endAddress == null ? "" : endAddress
        );
        Slice<Yata> yataList = yataSearchService.findYataByLocation(
                mapper.postToLocation(startLocationDto),
                mapper.postToLocation(endLocationDto),
                distance,
                yataStatus == null ? null : YataStatus.valueOf(yataStatus),
                pageable
        );
        //mapper.yatasToYataResponses(yataList)
        return ResponseEntity.ok(new SliceResponseDto<>(
                mapper.yatasToYataResponses(yataList.getContent()),
                new SliceInfo(pageable, yataList.getNumberOfElements(), yataList.hasNext())
        ));
    }
}

메서드 설명

위에 만들어놓은 메서드들을 이용하여 /api/v1/yata/search/location경로의 GET 요청을 처리하는 메서드를 만든다.

출발지, 도착지, 거리 yataStatus(Nata/Neota), 페이지 정보를 기반으로 yata를 검색하고 변환하여 응답한다.

 

동작 방식
파라미터로 출발 위/경도, 도착 위/경도, 시작점 주소 도착점 주소를 받음(카풀 서비스를 이용하려면 출발지와 목적지 두개의 위치가 필요)
그리고 설정 거리와 야타게시글 상태(운전자 게시글인지, 탑승자 게시글인지)를 받아옴 .

출발 지점,도착 지점을 각각LocationDto.Post객체로 만들어줌 위 경도값이 비어있다면 0을 넣어주고, 주소가 비어있따면 빈 문자열을 넣어줌

yataSearchService의 yataByLocatino 메서드를 이용하여 yataList를 가져옴

시작 위치 , 도착 위치, 거리 , 야타상태 페이지 정보를 전달하여 야타를 컴색, 반환값은 Slice<Yata>

mapper.yatasToYataResponses(yataList)//받아온 yataList를 SliceResponseDto객체를 생성하여 ResposneEntity.ok()를 통해 응답

 

 

 

 

이제 출발지와 도착지로 해당 범위(distance) 안에 존재하는 위치의 yata게시글을 가져올 수 있다.

 

 

 

해당 기능에서 사용한 Hibernate spatial 라이브러리에 대한 추가 블로깅도 하였다.
조금 더 자세한 예시와 기능들을 살펴보고 싶다면 요기

https://develoyummer.tistory.com/117

 

Hibernate spatial이란? / 간단한 쿼리 메서드

Hibernate Spatial은 지리 데이터 작업을 위한 표준 인터페이스를 제공한다 . 지리적 데이터에는 점, 선, 다각형 과 같은 엔터티의 표현이 포함됨 . Hibernate Spatial은 기하학 모델로 JTS(Java Topology Suite)를

develoyummer.tistory.com

 

 

다양한 공간 데이터 유형과 구조에 대한 블로깅은 요기!
https://develoyummer.tistory.com/118

 

d

https://www.baeldung.com/java-geospatial-applications https://t1.daumcdn.net/cfile/tistory/24338140578C65662D?original GIS 애플리케이션 구축 시 요구사항 1.지리 공간 데이터에 대한 실시간 분석을 하는 경우가 많음 수많은

develoyummer.tistory.com

 

 

 

 

 

참고자료

https://www.tabnine.com/code/java/methods/org.locationtech.jts.geom.Geometry/buffer

https://velog.io/@hwsa1004/Spring-%EC%9C%84%EC%B9%98-%EA%B8%B0%EB%B0%98-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0

https://wooody92.github.io/project/JPA%EC%99%80-MySQL%EB%A1%9C-%EC%9C%84%EC%B9%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A4%EB%A3%A8%EA%B8%B0/

복사했습니다!