Skip to content

Commit

Permalink
Merge pull request #78 from HongDam-org/feat/redis-geolocation
Browse files Browse the repository at this point in the history
[FEAT] redis geolocation을 사용한 좌표 거리 계산
  • Loading branch information
ohksj77 authored Dec 23, 2023
2 parents 566007d + 7ab9e31 commit b0b62f5
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 66 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.twtw.backend.domain.location.dto.collection;

import lombok.RequiredArgsConstructor;

import org.springframework.data.geo.Point;

import java.util.List;

@RequiredArgsConstructor
public class MemberDistances {

private static final double DEFAULT_VALUE = 0.0;

private final List<Point> points;

public double averageLongitude() {
return points.stream().mapToDouble(Point::getX).average().orElse(DEFAULT_VALUE);
}

public double averageLatitude() {
return points.stream().mapToDouble(Point::getY).average().orElse(DEFAULT_VALUE);
}
}
Original file line number Diff line number Diff line change
@@ -1,110 +1,70 @@
package com.twtw.backend.domain.location.service;

import com.twtw.backend.domain.location.dto.collection.MemberDistances;
import com.twtw.backend.domain.location.dto.request.LocationRequest;
import com.twtw.backend.domain.location.dto.response.AverageCoordinate;
import com.twtw.backend.domain.member.entity.Member;
import com.twtw.backend.domain.plan.entity.Plan;

import lombok.RequiredArgsConstructor;

import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResult;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class GeoService {

private static final int CURRENT_LOCATION_INDEX = 0;
private final RedisTemplate<String, String> redisTemplate;

public AverageCoordinate saveLocation(
final Plan plan, final Member member, final LocationRequest locationRequest) {
final String planId = plan.getId().toString();
final String memberId = member.getId().toString();

redisTemplate
.opsForGeo()
.add(
planId,
new Point(locationRequest.getLongitude(), locationRequest.getLatitude()),
member.getId().toString());
memberId);

return calculate(planId, locationRequest);
return calculateAverage(collectMemberDistances(planId), planId, memberId);
}

private AverageCoordinate calculate(
final String planId, final LocationRequest locationRequest) {
final Double userLongitude = locationRequest.getLongitude();
final Double userLatitude = locationRequest.getLatitude();

GeoResults<GeoLocation<String>> geoResults =
redisTemplate
.opsForGeo()
.radius(
planId,
new Circle(
new Point(userLongitude, userLatitude),
new Distance(0, Metrics.KILOMETERS)));

if (geoResults == null) {
return new AverageCoordinate();
}

final List<GeoResult<GeoLocation<String>>> content = geoResults.getContent();

if (content.isEmpty()) {
return new AverageCoordinate();
}

return calculateAverage(content, userLatitude, userLongitude);
private MemberDistances collectMemberDistances(final String planId) {
return redisTemplate.opsForSet().members(planId).stream()
.map(
member ->
redisTemplate
.opsForGeo()
.position(planId, member)
.get(CURRENT_LOCATION_INDEX))
.collect(Collectors.collectingAndThen(Collectors.toList(), MemberDistances::new));
}

private AverageCoordinate calculateAverage(
final List<GeoResult<GeoLocation<String>>> content,
final Double userLatitude,
final Double userLongitude) {

double totalLatitude = 0.0;
double totalLongitude = 0.0;
final MemberDistances memberDistances, final String planId, final String memberId) {

for (GeoResult<GeoLocation<String>> geoResult : content) {
Point point = geoResult.getContent().getPoint();
totalLatitude += point.getY();
totalLongitude += point.getX();
}
final double averageLongitude = memberDistances.averageLongitude();
final double averageLatitude = memberDistances.averageLatitude();

final int size = content.size();
double avgLatitude = totalLatitude / size;
double avgLongitude = totalLongitude / size;
redisTemplate.opsForGeo().add(planId, new Point(averageLongitude, averageLatitude), planId);

final Double distance = distance(userLatitude, userLongitude, avgLatitude, avgLongitude);
final Double distance = distance(planId, memberId);

return new AverageCoordinate(avgLongitude, avgLatitude, distance);
return new AverageCoordinate(averageLongitude, averageLatitude, distance);
}

private Double distance(
final Double lat1, final Double lon1, final Double lat2, final Double lon2) {
int radius = 6371;

double dLat = Math.toRadians(lat2 - lat1);
double dLon = Math.toRadians(lon2 - lon1);

double a =
Math.sin(dLat / 2) * Math.sin(dLat / 2)
+ Math.cos(Math.toRadians(lat1))
* Math.cos(Math.toRadians(lat2))
* Math.sin(dLon / 2)
* Math.sin(dLon / 2);

double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

return radius * c;
private double distance(final String planId, final String memberId) {
return redisTemplate
.opsForGeo()
.distance(planId, memberId, planId, Metrics.KILOMETERS)
.getValue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class LocationService {
public LocationResponse addInfo(final UUID planId, final LocationRequest locationRequest) {
final Member member = authService.getMemberByJwt();
final Plan plan = planService.getPlanEntity(planId);

plan.updateMemberLocation(
member, locationRequest.getLongitude(), locationRequest.getLatitude());

Expand Down

0 comments on commit b0b62f5

Please sign in to comment.