Skip to content

Commit

Permalink
feat: 대한민국을 2km단위로 나눠 해당 지역의 충전소 개수를 계산한다 (#880)
Browse files Browse the repository at this point in the history
* feat: 적당히 자르는 기능 구현

[#819]

* feat: 대한민국을 자르는 기능 구현

[#819]

* feat: grid 내의 충전소 개수를 저장하는 기능 추가

[#819]

* feat: 클러스터 기능 추가

* fix: 실패하는 테스트 수정

* feat: 혼잡도 계산 로직 중지

* refactor: 코드 리팩토링

* refactor: 코드 리팩토링

* refactor: 그리드 충전소 지정 속도 개선

* refactor: final 추가

* refactor: final 추가 및 불필요한 어노테이션 제거

* refactor: 메서드 분리 및 상수화

* refactor: 메서드 분리

* refactor: given-when-then 주석 추가

* refactor: 빈줄 삭제

* refactor: 반복 횟수 변경

* feat: grid의 마커 위치 정확도 수정

[#819]

* feat: 위도 경도 변경

[#819]

* infra: hikari CP size 변경

[#819]

* fix: 실패하는 테스트 수정

[#819]

* refactor: 메서드 분리

[#819]

* refactor: 싱글 스레드로 변경

[#819]

* refactor: 초기화 방식 변경

[#819]

* refactor: 초기화 방식 변경

[#819]

---------

Co-authored-by: drunkenhw <[email protected]>
  • Loading branch information
kiarakim and drunkenhw authored Oct 26, 2023
1 parent 987a02e commit 4390673
Show file tree
Hide file tree
Showing 24 changed files with 681 additions and 23 deletions.
Empty file added backend/src/README.md
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.carffeine.carffeine.station.config;

import com.carffeine.carffeine.station.service.station.StationGridCacheService;
import com.carffeine.carffeine.station.service.station.StationGridFacadeService;
import com.carffeine.carffeine.station.service.station.dto.GridWithCount;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Slf4j
@RequiredArgsConstructor
@Configuration
public class InitialStationGridLoader implements ApplicationRunner {

private final StationGridFacadeService stationGridFacadeService;
private final StationGridCacheService stationGridCacheService;

@Override
public void run(ApplicationArguments args) {
log.info("initialize station grid");
List<GridWithCount> gridWithCounts = stationGridFacadeService.createGridWithCounts();
stationGridCacheService.initialize(gridWithCounts);
log.info("cache size : {}", gridWithCounts.size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.StationSimpleResponse;
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.StationSpecificResponse;
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.StationSummaryResponse;
import com.carffeine.carffeine.station.service.station.StationGridCacheService;
import com.carffeine.carffeine.station.service.station.StationGridFacadeService;
import com.carffeine.carffeine.station.service.station.StationQueryService;
import com.carffeine.carffeine.station.service.station.dto.ClusterRequest;
import com.carffeine.carffeine.station.service.station.dto.CoordinateRequest;
import com.carffeine.carffeine.station.service.station.dto.GridWithCount;
import com.carffeine.carffeine.station.service.station.dto.StationsSearchResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -26,6 +30,7 @@
public class StationController {

private final StationQueryService stationQueryService;
private final StationGridFacadeService stationGridFacadeService;

@GetMapping("/stations")
public ResponseEntity<StationsSimpleResponse> getStations(CoordinateRequest request,
Expand All @@ -36,6 +41,12 @@ public ResponseEntity<StationsSimpleResponse> getStations(CoordinateRequest requ
return ResponseEntity.ok(new StationsSimpleResponse(simpleResponses));
}

@GetMapping("/stations/clusters")
public ResponseEntity<List<GridWithCount>> getStationsClusters(ClusterRequest request) {
List<GridWithCount> counts = stationGridFacadeService.counts(request);
return ResponseEntity.ok(counts);
}

@GetMapping("/stations/search")
public ResponseEntity<StationsSearchResponse> searchStations(@RequestParam(value = "q") String query,
@RequestParam(value = "scope") Set<String> scope,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.carffeine.carffeine.station.domain.station;

import lombok.Getter;
import lombok.ToString;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;

@ToString
@Getter
public class Grid {

private static final BigDecimal HALF = BigDecimal.valueOf(2);
private static final Random RANDOM = new Random();

private final String id;
private final Point top;
private final Point bottom;
private final List<Point> points;
private int count;

public Grid(Point top, Point bottom) {
this.id = UUID.randomUUID().toString();
this.top = top;
this.bottom = bottom;
this.points = new ArrayList<>();
}

public boolean isContain(Point point) {
Latitude topLatitude = top.getLatitude();
Latitude bottomLatitude = bottom.getLatitude();
Latitude pointLatitude = point.getLatitude();
Longitude topLongitude = top.getLongitude();
Longitude bottomLongitude = bottom.getLongitude();
Longitude pointLongitude = point.getLongitude();

if (topLatitude.isHigher(bottomLatitude) && (pointLatitude.isBetween(topLatitude, bottomLatitude))) {
return pointLongitude.isBetween(topLongitude, bottomLongitude);
}
return pointLongitude.isBetween(bottomLongitude, topLongitude);
}

public BigDecimal calculateCenterLatitude() {
Latitude topLatitude = top.getLatitude();
Latitude bottomLatitude = bottom.getLatitude();
BigDecimal latitudeDistance = topLatitude.add(bottomLatitude);
return latitudeDistance.divide(HALF, 4, RoundingMode.CEILING);
}

public BigDecimal calculateCenterLongitude() {
Longitude topLongitude = top.getLongitude();
Longitude bottomLongitude = bottom.getLongitude();
BigDecimal longitudeDistance = topLongitude.add(bottomLongitude);
return longitudeDistance.divide(HALF, 4, RoundingMode.CEILING);
}

public Point randomPoint() {
int randomIndex = RANDOM.nextInt(points.size());

return points.get(randomIndex);
}

public void addPoint(Point point) {
points.add(point);
}

public int stationSize() {
return points.size();
}

public boolean hasStation() {
return !points.isEmpty();
}

public boolean existsCount() {
return count > 0;
}

public void addCount(int count) {
this.count += count;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.carffeine.carffeine.station.domain.station;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

public class GridGenerator {

private static final BigDecimal TOP_LATITUDE = BigDecimal.valueOf(38.6341);
private static final BigDecimal TOP_LONGITUDE = BigDecimal.valueOf(124.5377);
private static final BigDecimal BOTTOM_LATITUDE = BigDecimal.valueOf(33.1906);
private static final BigDecimal BOTTOM_LONGITUDE = BigDecimal.valueOf(131.8795);

public List<Grid> create(Point top, Point bottom, int latitudeDivisionSize, int longitudeDivisionSize) {

List<Grid> grids = new ArrayList<>();
List<Latitude> latitudes = divideLatitude(top, bottom, latitudeDivisionSize);
List<Longitude> longitudes = divideLongitude(top, bottom, longitudeDivisionSize);
BigDecimal latInterval = getLatInterval(top, bottom, latitudeDivisionSize);
BigDecimal longInterval = getLongInterval(top, bottom, longitudeDivisionSize);
for (Latitude latitude : latitudes) {
for (Longitude longitude : longitudes) {
Point topPoint = Point.of(latitude.getValue(), longitude.getValue());
Point bottomPoint = Point.of(latitude.getValue().add(latInterval), longitude.getValue().add(longInterval));
grids.add(new Grid(topPoint, bottomPoint));
}
}
return grids;
}

private BigDecimal getLongInterval(Point top, Point bottom, int divisionSize) {
Longitude topLongitude = top.getLongitude();
Longitude bottomLongitude = bottom.getLongitude();
BigDecimal length = topLongitude.subtract(bottomLongitude);
return length.divide(BigDecimal.valueOf(divisionSize), 4, RoundingMode.HALF_EVEN);
}

private BigDecimal getLatInterval(Point top, Point bottom, int divisionSize) {
Latitude topLatitude = top.getLatitude();
Latitude bottomLatitude = bottom.getLatitude();
BigDecimal length = topLatitude.subtract(bottomLatitude);
return length.divide(BigDecimal.valueOf(divisionSize), 4, RoundingMode.HALF_EVEN);
}

private List<Longitude> divideLongitude(Point top, Point bottom, int divisionSize) {
Longitude topLongitude = top.getLongitude();
Longitude bottomLongitude = bottom.getLongitude();
BigDecimal length = topLongitude.subtract(bottomLongitude);
BigDecimal interval = length.divide(BigDecimal.valueOf(divisionSize), 4, RoundingMode.HALF_EVEN);
return IntStream.range(0, divisionSize)
.mapToObj(index -> calculateGridLongitude(index, bottomLongitude, interval))
.toList();
}

private Longitude calculateGridLongitude(int index, Longitude longitude, BigDecimal interval) {
BigDecimal distance = interval.multiply(new BigDecimal(index));
return Longitude.from(longitude.getValue().add(distance));

}

private List<Latitude> divideLatitude(Point top, Point bottom, int divisionSize) {
Latitude topLatitude = top.getLatitude();
Latitude bottomLatitude = bottom.getLatitude();
BigDecimal length = topLatitude.subtract(bottomLatitude);
BigDecimal interval = length.divide(BigDecimal.valueOf(divisionSize), 4, RoundingMode.HALF_EVEN);
return IntStream.range(0, divisionSize)
.mapToObj(index -> calculateGridLatitude(index, bottomLatitude, interval))
.toList();
}

private Latitude calculateGridLatitude(int index, Latitude latitude, BigDecimal interval) {
BigDecimal distance = interval.multiply(new BigDecimal(index));
return Latitude.from(latitude.getValue().add(distance));
}

public List<Grid> createKorea() {
Point top = new Point(Latitude.from(TOP_LATITUDE), Longitude.from(TOP_LONGITUDE));
Point bottom = new Point(Latitude.from(BOTTOM_LATITUDE), Longitude.from(BOTTOM_LONGITUDE));
return create(top, bottom, 300, 240);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.carffeine.carffeine.station.domain.station;

import com.carffeine.carffeine.station.exception.StationException;
import com.carffeine.carffeine.station.exception.StationExceptionType;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.math.BigDecimal;

@ToString
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "value")
Expand Down Expand Up @@ -38,7 +38,7 @@ public static Latitude from(BigDecimal value) {

private void validateKoreaLatitude(BigDecimal value) {
if (value.compareTo(KOREA_MIN_LATITUDE) < 0 || value.compareTo(KOREA_MAX_LATITUDE) > 0) {
throw new StationException(StationExceptionType.INVALID_LATITUDE);
// throw new StationException(StationExceptionType.INVALID_LATITUDE);
}
}

Expand All @@ -49,4 +49,20 @@ public Latitude calculateMinLatitudeByDelta(BigDecimal delta) {
public Latitude calculateMaxLatitudeByDelta(BigDecimal delta) {
return new Latitude(value.add(delta));
}

public BigDecimal subtract(Latitude other) {
return value.subtract(other.value);
}

public boolean isHigher(Latitude other) {
return this.value.compareTo(other.value) > 0;
}

public boolean isBetween(Latitude top, Latitude bottom) {
return this.value.compareTo(top.value) <= 0 && this.value.compareTo(bottom.value) >= 0;
}

public BigDecimal add(Latitude other) {
return this.value.add(other.value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.math.BigDecimal;

@ToString
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(of = "value")
Expand Down Expand Up @@ -47,4 +49,20 @@ public Longitude calculateMinLongitudeByDelta(BigDecimal delta) {
public Longitude calculateMaxLongitudeByDelta(BigDecimal delta) {
return new Longitude(value.add(delta));
}

public BigDecimal subtract(Longitude other) {
return value.subtract(other.value);
}

public int compareTo(Longitude other) {
return this.value.compareTo(other.value);
}

public boolean isBetween(Longitude top, Longitude bottom) {
return this.value.compareTo(top.value) >= 0 && this.value.compareTo(bottom.value) <= 0;
}

public BigDecimal add(Longitude other) {
return this.value.add(other.value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.carffeine.carffeine.station.domain.station;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;

import java.math.BigDecimal;

@ToString
@EqualsAndHashCode
@Getter
@AllArgsConstructor
public class Point {

private final Latitude latitude;
private final Longitude longitude;

public static Point of(long latitude, long longitude) {
return new Point(Latitude.from(BigDecimal.valueOf(latitude)), Longitude.from(BigDecimal.valueOf(longitude)));
}

public static Point of(double latitude, double longitude) {
return new Point(Latitude.from(BigDecimal.valueOf(latitude)), Longitude.from(BigDecimal.valueOf(longitude)));
}

public static Point of(BigDecimal latitude, BigDecimal longitude) {
return new Point(Latitude.from(latitude), Longitude.from(longitude));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
public enum Region {

SEOUL("서울특별시", BigDecimal.valueOf(37.540705), BigDecimal.valueOf(126.956764)),
INCHEON("인천광역시", BigDecimal.valueOf(37.469221), BigDecimal.valueOf(126.573234)),
INCHEON("인천광역시", BigDecimal.valueOf(37.3865), BigDecimal.valueOf(126.647)),
GWANGJU("광주광역시", BigDecimal.valueOf(35.126033), BigDecimal.valueOf(126.831302)),
DAEGU("대구광역시", BigDecimal.valueOf(35.798838), BigDecimal.valueOf(128.583052)),
ULSAN("울산광역시", BigDecimal.valueOf(35.519301), BigDecimal.valueOf(129.239078)),
DAEJEON("대전광역시", BigDecimal.valueOf(36.321655), BigDecimal.valueOf(127.378953)),
BUSAN("부산광역시", BigDecimal.valueOf(35.198362), BigDecimal.valueOf(129.053922)),
GYEONGGI("경기도", BigDecimal.valueOf(37.567167), BigDecimal.valueOf(127.190292)),
GYEONGGI("경기도", BigDecimal.valueOf(37.2895), BigDecimal.valueOf(127.0535)),
GANGWON("강원특별자치도", BigDecimal.valueOf(37.555837), BigDecimal.valueOf(128.209315)),
CHUNGNAM("충청남도", BigDecimal.valueOf(36.557229), BigDecimal.valueOf(126.779757)),
CHUNGBUK("충청북도", BigDecimal.valueOf(36.628503), BigDecimal.valueOf(127.929344)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.ChargerSpecificResponse;
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.RegionMarker;
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.StationInfo;
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.StationPoint;
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.StationSearchResult;
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.StationSimpleResponse;
import com.carffeine.carffeine.station.infrastructure.repository.station.dto.StationSpecificResponse;
Expand Down Expand Up @@ -221,4 +222,21 @@ private RegionMarker getFetch(Region region) {
.where(station.address.startsWith(region.value()))
.fetchOne();
}

public List<StationPoint> findStationPoints(int page, int size) {
return jpaQueryFactory.select(constructor(StationPoint.class,
station.latitude.value,
station.longitude.value
))
.from(station)
.offset(size * page)
.limit(size)
.fetch();
}

public Long findStationCount() {
return jpaQueryFactory.select(station.stationId.count())
.from(station)
.fetchOne();
}
}
Loading

0 comments on commit 4390673

Please sign in to comment.