|
@@ -0,0 +1,222 @@
|
|
|
+package com.leromro.common.utils;
|
|
|
+
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
|
|
+import com.leromro.common.config.TencentMapProperties;
|
|
|
+import lombok.Data;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
+
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+@Component
|
|
|
+public class TencentMapUtils {
|
|
|
+
|
|
|
+ private final TencentMapProperties properties;
|
|
|
+ private final RestTemplate restTemplate;
|
|
|
+ private static final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+ static {
|
|
|
+ // 忽略未知属性
|
|
|
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
|
|
+ // 添加以下配置即可支持下划线转驼峰
|
|
|
+ objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
|
|
|
+ }
|
|
|
+
|
|
|
+ public TencentMapUtils(TencentMapProperties properties, RestTemplate restTemplate) {
|
|
|
+ this.properties = properties;
|
|
|
+ this.restTemplate = restTemplate;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====== 地理编码(地址 -> 经纬度)======
|
|
|
+ public GeocodeResult geocode(String address) throws Exception {
|
|
|
+ String url = properties.getBaseUrl() + "/ws/geocoder/v1/?address={address}&key={key}";
|
|
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
+ params.put("address", address);
|
|
|
+ params.put("key", properties.getKey());
|
|
|
+
|
|
|
+ String response = restTemplate.getForObject(url, String.class, params);
|
|
|
+ return parseResponse(response, GeocodeResult.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====== 逆地理编码(经纬度 -> 地址)======
|
|
|
+ public ReverseGeocodeResult reverseGeocode(double latitude, double longitude) {
|
|
|
+ String location = latitude + "," + longitude;
|
|
|
+ String url = properties.getBaseUrl() + "/ws/geocoder/v1/?location={location}&key={key}";
|
|
|
+
|
|
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
+ params.put("location", location);
|
|
|
+ params.put("key", properties.getKey());
|
|
|
+
|
|
|
+ String response = restTemplate.getForObject(url, String.class, params);
|
|
|
+ return parseResponse(response, ReverseGeocodeResult.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====== 距离计算 ======
|
|
|
+ public DistanceResult calculateDistance(List<String> origins, List<String> destinations) throws Exception {
|
|
|
+ String originsStr = String.join(";", origins);
|
|
|
+ String destsStr = String.join(";", destinations);
|
|
|
+
|
|
|
+ String url = properties.getBaseUrl() + "/ws/distance/v1/?origins={origins}&destinations={destinations}&key={key}";
|
|
|
+
|
|
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
+ params.put("origins", originsStr);
|
|
|
+ params.put("destinations", destsStr);
|
|
|
+ params.put("key", properties.getKey());
|
|
|
+
|
|
|
+ String response = restTemplate.getForObject(url, String.class, params);
|
|
|
+ return parseResponse(response, DistanceResult.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====== 周边搜索 ======
|
|
|
+ public PlaceSearchResult searchNearby(String keyword, double latitude, double longitude, int radius) throws Exception {
|
|
|
+ String boundary = "nearby(" + latitude + "," + longitude + "," + radius + ")";
|
|
|
+ String url = properties.getBaseUrl() + "/ws/place/v1/search?keyword={keyword}&boundary={boundary}&key={key}";
|
|
|
+
|
|
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
+ params.put("keyword", keyword);
|
|
|
+ params.put("boundary", boundary);
|
|
|
+ params.put("key", properties.getKey());
|
|
|
+
|
|
|
+ String response = restTemplate.getForObject(url, String.class, params);
|
|
|
+ return parseResponse(response, PlaceSearchResult.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====== 路径规划(驾车)=====
|
|
|
+ public DirectionResult getDrivingRoute(double fromLat, double fromLng, double toLat, double toLng) throws Exception {
|
|
|
+ String from = fromLat + "," + fromLng;
|
|
|
+ String to = toLat + "," + toLng;
|
|
|
+ String url = properties.getBaseUrl() + "/ws/direction/v1/driving/?from={from}&to={to}&key={key}";
|
|
|
+
|
|
|
+ Map<String, Object> params = new HashMap<>();
|
|
|
+ params.put("from", from);
|
|
|
+ params.put("to", to);
|
|
|
+ params.put("key", properties.getKey());
|
|
|
+
|
|
|
+ String response = restTemplate.getForObject(url, String.class, params);
|
|
|
+ return parseResponse(response, DirectionResult.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====== 工具方法 ======
|
|
|
+ private <T> T parseResponse(String response, Class<T> clazz) {
|
|
|
+ try {
|
|
|
+ return objectMapper.readValue(response, clazz);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ====== 数据结构定义 ======
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class GeocodeResult {
|
|
|
+ private int status;
|
|
|
+ private String message;
|
|
|
+ private Result result;
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class Result {
|
|
|
+ private Location location;
|
|
|
+ private String address;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class Location {
|
|
|
+ private double lat;
|
|
|
+ private double lng;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class ReverseGeocodeResult {
|
|
|
+ private int status;
|
|
|
+ private String message;
|
|
|
+ private Result result;
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class Result {
|
|
|
+ private String address;
|
|
|
+ private AddressComponent addressComponent; // 地址组件
|
|
|
+ private AdInfo adInfo; // 行政区划信息
|
|
|
+ private Location location;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class AddressComponent {
|
|
|
+ private String nation;
|
|
|
+ private String province;
|
|
|
+ private String city;
|
|
|
+ private String district;
|
|
|
+ private String street;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class AdInfo {
|
|
|
+ private String adcode; // 行政区划编码
|
|
|
+ private String province; // 省
|
|
|
+ private String city; // 市
|
|
|
+ private String district; // 区
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class Location {
|
|
|
+ private double lat;
|
|
|
+ private double lng;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class DistanceResult {
|
|
|
+ private int status;
|
|
|
+ private String message;
|
|
|
+ private List<DistanceElement> result;
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class DistanceElement {
|
|
|
+ private int distance; // 单位:米
|
|
|
+ private String duration; // 时间描述
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class PlaceSearchResult {
|
|
|
+ private int status;
|
|
|
+ private String message;
|
|
|
+ private List<Poi> data;
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class Poi {
|
|
|
+ private String title;
|
|
|
+ private Location location;
|
|
|
+ private String address;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class Location {
|
|
|
+ private double lat;
|
|
|
+ private double lng;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class DirectionResult {
|
|
|
+ private int status;
|
|
|
+ private String message;
|
|
|
+ private RouteResult result;
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class RouteResult {
|
|
|
+ private Route route;
|
|
|
+
|
|
|
+ @Data
|
|
|
+ public static class Route {
|
|
|
+ private double distance; // 总距离(单位:米)
|
|
|
+ private double duration; // 总时间(单位:秒)
|
|
|
+ private String instruction; // 导航说明
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|