package com.izouma.walkchina.service; import com.github.kevinsawicki.http.HttpRequest; import com.google.gson.Gson; import com.izouma.walkchina.constant.AppConstants; import com.izouma.walkchina.domain.City; import com.izouma.walkchina.domain.JourneyStage; import com.izouma.walkchina.domain.UserInfo; import com.izouma.walkchina.domain.UserJourney; import com.izouma.walkchina.dto.Location; import com.izouma.walkchina.dto.MapRegion; import com.izouma.walkchina.dto.UserMap; import com.izouma.walkchina.dto.UserMarker; import com.izouma.walkchina.dto.webservice.DirectionResponse; import com.izouma.walkchina.dto.webservice.RouteStep; import com.izouma.walkchina.exception.ServiceException; import com.izouma.walkchina.repo.*; import com.izouma.walkchina.utils.SecurityUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.util.*; import static java.time.temporal.ChronoUnit.DAYS; @Service @Slf4j public class MapService { @Autowired private CityRepository cityRepository; @Autowired private UserJourneyRepository userJourneyRepository; @Autowired private JourneyStageRepository journeyStageRepository; @Autowired private UserInfoRepository userInfoRepository; @Autowired private StageAwardRepository stageAwardRepository; @Autowired private WalkDataRepository walkDataRepository; @Value("${aliyun.oss_domain}") private String ossDomain; public List citiesInRegion(MapRegion mapRegion) { Specification specification = (Specification) (root, criteriaQuery, criteriaBuilder) -> { return criteriaBuilder.and(criteriaBuilder.between(root.get("longitude"), mapRegion.getSouthwest().getLongitude(), mapRegion.getNortheast().getLongitude()), criteriaBuilder.between(root.get("latitude"), mapRegion.getSouthwest().getLatitude(), mapRegion.getNortheast().getLatitude())); }; return cityRepository.findAll(specification); } public List findNearCities(Long id, Long userId) { return cityRepository.findNear(id, userId); } public Collection usersInRegion(Long userId, MapRegion mapRegion) { List list = userInfoRepository.findInRegion(userId, mapRegion.getSouthwest().getLatitude(), mapRegion.getSouthwest().getLongitude(), mapRegion.getNortheast().getLongitude(), mapRegion.getNortheast().getLongitude()); for (UserMarker userMarker : list) { userMarker.setCanSteal(stageAwardRepository.countAllByUserIdAndReceivedEquals(userMarker.getUserId(), false) > 1); userMarker.setIcon(ossDomain + "/marker/user/" + userMarker.getUserId() + ".png"); } return list; } public DirectionResponse direction(Location from, Location to) { Map params = new HashMap<>(); params.put("from", from.getLatitude() + "," + from.getLongitude()); params.put("to", to.getLatitude() + "," + to.getLongitude()); params.put("key", "YO4BZ-G75L5-CWJIV-QDPOY-77OIH-LGFMT"); String body = HttpRequest.get("https://apis.map.qq.com/ws/direction/v1/walking/", params, false).body(); Gson gson = new Gson(); return gson.fromJson(body, DirectionResponse.class); } public UserMap userMap(Long userId) { UserJourney userJourney = Optional.ofNullable(userJourneyRepository.findByUserId(userId)).orElseThrow(new ServiceException("无记录")); City origin = Optional.ofNullable(userJourney.getOrigin()).orElseThrow(new ServiceException("无记录")); City destination = Optional.ofNullable(userJourney.getDestination()).orElseThrow(new ServiceException("无记录")); UserInfo userInfo = userInfoRepository.findById(userId).orElseThrow(new ServiceException("用户不存在")); UserMap userMap = UserMap.builder() .polyline(minifyPolyline(userJourney.getPolyline())) .origin(origin) .destination(destination) .build(); List progressPolyline = new ArrayList<>(); List stages = journeyStageRepository.findAllByJourneyIdOrderById(userJourney.getId()); for (int i = 0; i < stages.size() - 1; i++) { progressPolyline.addAll(stages.get(i).getPolyline()); } JourneyStage latestStage = stages.get(stages.size() - 1); userMap.setDestination(latestStage.getDestination()); userMap.setNearOrigin(latestStage.getOrigin()); userMap.setProgress(latestStage.getProgress()); userMap.setCurrentSteps(latestStage.getCurrentSteps()); userMap.setStageAwards(stageAwardRepository.findAllByUserIdAndStageId(userId, latestStage.getId())); userMap.setDays((int) DAYS.between(latestStage.getCreatedAt().toLocalDate(), LocalDate.now()) + 1); List steps = latestStage.getRouteSteps(); // int distance = 0; // for (RouteStep step : steps) { // distance += step.getDistance(); // if (distance < latestStage.getDistance() * latestStage.getProgress()) { // for (int j = step.getPolylineIdx().get(0); j <= step.getPolylineIdx().get(1); j++) { // progressPolyline.add(latestStage.getPolyline().get(j)); // } // } else { // double p = 1 - (distance - (latestStage.getDistance() * latestStage.getProgress())) / (double) step.getDistance(); // int start = step.getPolylineIdx().get(0); // int end = start + (int) ((step.getPolylineIdx().get(1) - step.getPolylineIdx().get(0)) * p); // end = end + (end % 2 == 0 ? 1 : 0); // if (end < start + 1) { // end = start + 1; // } // if (end > step.getPolylineIdx().get(1)) { // end = step.getPolylineIdx().get(1); // } // // for (int j = start; j <= end; j++) { // progressPolyline.add(latestStage.getPolyline().get(j)); // } // break; // } // } for (int i = 0; i <= endPoint(steps, latestStage.getDistance() * latestStage.getProgress()); i++) { progressPolyline.add(latestStage.getPolyline().get(i)); } userMap.setProgressPolyline(minifyPolyline(progressPolyline)); return userMap; } int endPoint(List steps, double distance) { int d = 0; for (RouteStep step : steps) { d += step.getDistance(); if (d >= distance) { double p = 1 - (d - distance) / step.getDistance(); int start = step.getPolylineIdx().get(0); int end = start + (int) ((step.getPolylineIdx().get(1) - step.getPolylineIdx().get(0)) * p); end = end + (end % 2 == 0 ? 1 : 0); if (end < start + 1) { end = start + 1; } if (end > step.getPolylineIdx().get(1)) { end = step.getPolylineIdx().get(1); } return end; } } return steps.get(steps.size() - 1).getPolylineIdx().get(1); } List extractPolyline(List polyline) { List list = new ArrayList<>(polyline); for (int i = 2; i < list.size(); i++) { list.set(i, BigDecimal.valueOf(list.get(i - 2) + list.get(i) / 1000000).setScale(6, RoundingMode.HALF_EVEN).doubleValue()); } return list; } List compressPolyline(List polyline) { List list = new ArrayList<>(polyline); for (int i = 2; i < list.size(); i++) { list.set(i, Math.round(polyline.get(i) * 1000000d - polyline.get(i - 2) * 1000000d)); } return list; } private List minifyPolyline(List polyline) { List list = new ArrayList<>(); List extracted = extractPolyline(polyline); list.add(polyline.get(0)); list.add(polyline.get(1)); for (int i = 2; i < polyline.size(); i += 20) { double d0 = Math.abs(polyline.get(i)); double d1 = Math.abs(polyline.get(i + 1)); if (d0 == 0 && d1 == 0) { continue; } if (d0 < 20 && d1 < 20) { continue; } list.add(extracted.get(i)); list.add(extracted.get(i + 1)); } return compressPolyline(list); } public Map calcAward(Long originId, Long destinationId) { City origin = cityRepository.findById(originId).orElseThrow(new ServiceException("无记录")); City destination = cityRepository.findById(destinationId).orElseThrow(new ServiceException("无记录")); DirectionResponse response = direction(new Location(origin.getLatitude(), origin.getLongitude()), new Location(destination.getLatitude(), destination.getLongitude())); if (response.getStatus() != 0) { throw new ServiceException(response.getMessage()); } Integer distance = response.getResult().getRoutes().get(0).getDistance(); Long step = Math.round(distance / AppConstants.STEP_TO_DISTANCE_RATE); UserInfo userInfo = userInfoRepository.findById(SecurityUtils.getAuthenticatedUser().getId()).orElseThrow(new ServiceException("用户不存在")); BigDecimal award = userInfo.getPrice(); Map map = new HashMap<>(); map.put("step", step); map.put("award", award); return map; } }