package com.xiaopeng.map.utils; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.xiaopeng.api.dto.operation.LonLatDto; import com.xiaopeng.api.vo.map.PointByIpVo; import com.xiaopeng.common.core.exception.ServiceException; import com.xiaopeng.common.core.utils.HttpUtils; import com.xiaopeng.map.constant.I18nKeyConstant; import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import org.springframework.beans.factory.annotation.Value; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * 高德地图步行导航工具类 */ @Slf4j public class AmapWalkingRouteUtil { // 高德地图Web服务API配置 private static final String AMAP_API_BASE_URL = "https://restapi.amap.com/v3/direction/walking"; private static final String AMAP_GEOCODING_URL = "https://restapi.amap.com/v3/geocode/geo"; // 高德API基础URL private static final String IP_API_URL = "https://restapi.amap.com/v3/ip"; private static final String GEOCODE_API_URL = "https://restapi.amap.com/v3/geocode/geo"; // HTTP客户端 private final OkHttpClient httpClient; private final ObjectMapper objectMapper; // @Value("${map.apikey}") private static String apiKey="66ae577f63898dae3016d599fda57221"; /** * IP定位 */ public static PointByIpVo ipLocation(String ip) throws IOException { PointByIpVo pointByIpVo = new PointByIpVo(); StringBuilder urlBuilder = new StringBuilder(IP_API_URL) .append("?key=").append(apiKey) .append("&output=json"); if (ip != null && !ip.trim().isEmpty()) { urlBuilder.append("&ip=").append(ip); } String response = HttpUtils.sendGet(urlBuilder.toString()); JSONObject jsonObject = JSONObject.parseObject(response); // 城市名称 String city = jsonObject.getString("city"); String province = jsonObject.getString("province"); if(("局域网").equals(province)){ pointByIpVo.setCity("成都市"); LonLatDto lonLatDto = new LonLatDto(104.07, 30.660); pointByIpVo.setPoint(lonLatDto); pointByIpVo.setAdcode("510100"); return pointByIpVo; } String adcode = jsonObject.getString("adcode"); if (city == null || city.trim().isEmpty() || "[]".equals(city)) { // 直辖市:adcode前两位是11,12,31,50 if (adcode != null) { String prefix = adcode.substring(0, 2); if ("11".equals(prefix)) { city = "北京市"; } if ("12".equals(prefix)) { city = "天津市"; } if ("31".equals(prefix)) { city = "上海市"; } if ("50".equals(prefix)) { city = "重庆市"; } // 如果没有city但有province,使用省份 city = province; } } LonLatDto lonLatDto = geocodeCity(city); pointByIpVo.setCity(city); pointByIpVo.setPoint(lonLatDto); pointByIpVo.setAdcode(adcode); return pointByIpVo; } private static LonLatDto geocodeCity(String cityName) throws IOException { String url = GEOCODE_API_URL + "?key=" +apiKey + "&address=" + cityName + "&output=json&city=" + cityName; String response = HttpUtils.sendGet(url); System.out.println(response); JSONObject jsonObject = JSONObject.parseObject(response); // 获取geocodes数组 JSONArray geocodes = jsonObject.getJSONArray("geocodes"); if (geocodes == null || geocodes.isEmpty()) { return null; } // 取第一个geocode JSONObject firstGeocode = geocodes.getJSONObject(0); // 获取location字段 String lonLat = firstGeocode.getString("location"); String[] lonLatArray = lonLat.split(","); double longitude = Double.parseDouble(lonLatArray[0]); double latitude = Double.parseDouble(lonLatArray[1]); return new LonLatDto(longitude, latitude); } public static void main (String[]args) throws IOException { geocodeCity("凉山彝族自治州"); } /** * 构造函数 * @param apiKey 高德地图API Key */ public AmapWalkingRouteUtil(String apiKey) { this.apiKey = apiKey; this.httpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build(); this.objectMapper = new ObjectMapper(); } /** * 位置坐标类 */ public static class Location { private final double longitude; // 经度 private final double latitude; // 纬度 public Location(double longitude, double latitude) { this.longitude = longitude; this.latitude = latitude; } public double getLongitude() { return longitude; } public double getLatitude() { return latitude; } @Override public String toString() { return String.format("%.6f,%.6f", longitude, latitude); } } /** * 步行路线结果类 */ public static class WalkingRouteResult { private int duration; // 步行时间(秒) private int distance; // 步行距离(米) private String origin; // 起点地址 private String destination; // 终点地址 private boolean success; // 是否成功 private String message; // 返回信息 // getters and setters public int getDuration() { return duration; } public void setDuration(int duration) { this.duration = duration; } public int getDistance() { return distance; } public void setDistance(int distance) { this.distance = distance; } public String getOrigin() { return origin; } public void setOrigin(String origin) { this.origin = origin; } public String getDestination() { return destination; } public void setDestination(String destination) { this.destination = destination; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } /** * 获取格式化的步行时间 * @return 格式化的时间字符串(如:15分钟30秒) */ public String getFormattedDuration() { int seconds = duration; int minutes = seconds / 60; seconds = seconds % 60; return String.format("%d分%d秒", minutes, seconds); } /** * 获取格式化的步行距离 * @return 格式化的距离字符串(如:1.2公里) */ public String getFormattedDistance() { if (distance >= 1000) { double km = distance / 1000.0; return String.format("%.1f公里", km); } else { return String.format("%d米", distance); } } } /** * 根据地址获取经纬度坐标 * @param address 地址信息 * @return 经纬度坐标 */ public Location getLocationByAddress (String address) throws IOException { Map params = new HashMap<>(); params.put("key", apiKey); params.put("address", address); String url = buildUrl(AMAP_GEOCODING_URL, params); Request request = new Request.Builder().url(url).build(); try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("请求失败: " + response); } String responseBody = response.body().string(); JsonNode rootNode = objectMapper.readTree(responseBody); if ("1".equals(rootNode.get("status").asText())) { JsonNode geocodes = rootNode.get("geocodes"); if (geocodes != null && geocodes.size() > 0) { String locationStr = geocodes.get(0).get("location").asText(); String[] locationArray = locationStr.split(","); double longitude = Double.parseDouble(locationArray[0]); double latitude = Double.parseDouble(locationArray[1]); return new Location(longitude, latitude); } } throw new IOException("地址解析失败: " + rootNode.get("info").asText()); } } /** * 获取步行路线和时间 * @param origin 起点坐标 * @param destination 终点坐标 * @return 步行路线结果 */ public WalkingRouteResult getWalkingRoute (Location origin, Location destination){ WalkingRouteResult result = new WalkingRouteResult(); try { Map params = new HashMap<>(); params.put("key", apiKey); params.put("origin", origin.toString()); params.put("destination", destination.toString()); String url = buildUrl(AMAP_API_BASE_URL, params); Request request = new Request.Builder().url(url).build(); try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { result.setSuccess(false); result.setMessage("HTTP请求失败: " + response.code()); return result; } String responseBody = response.body().string(); JsonNode rootNode = objectMapper.readTree(responseBody); String status = rootNode.get("status").asText(); String info = rootNode.get("info").asText(); if ("1".equals(status)) { JsonNode route = rootNode.get("route"); JsonNode paths = route.get("paths"); if (paths != null && paths.size() > 0) { JsonNode firstPath = paths.get(0); int duration = firstPath.get("duration").asInt(); int distance = firstPath.get("distance").asInt(); result.setDuration(duration); result.setDistance(distance); result.setSuccess(true); result.setMessage("成功获取步行路线"); // 尝试获取地址信息 result.setOrigin(getAddressByLocation(origin)); result.setDestination(getAddressByLocation(destination)); } else { result.setSuccess(false); result.setMessage("未找到步行路线"); log.info("未找到步行路线"); throw new ServiceException("未找到步行路线", I18nKeyConstant.NO_WALKING_ROUTE_FOUND); } } else { result.setSuccess(false); result.setMessage("API返回错误: " + info); log.info("API返回错误" + info); throw new ServiceException("未找到步行路线", I18nKeyConstant.NO_WALKING_ROUTE_FOUND); } } } catch (Exception e) { result.setSuccess(false); result.setMessage("获取步行路线失败: " + e.getMessage()); log.info("获取步行路线失败" + e.getMessage()); throw new ServiceException("未找到步行路线", I18nKeyConstant.NO_WALKING_ROUTE_FOUND); } return result; } /** * 根据经纬度获取地址(逆地理编码) * @param location 经纬度坐标 * @return 地址信息 */ private String getAddressByLocation (Location location){ try { Map params = new HashMap<>(); params.put("key", apiKey); params.put("location", location.toString()); String url = buildUrl("https://restapi.amap.com/v3/geocode/regeo", params); Request request = new Request.Builder().url(url).build(); try (Response response = httpClient.newCall(request).execute()) { if (response.isSuccessful()) { String responseBody = response.body().string(); JsonNode rootNode = objectMapper.readTree(responseBody); if ("1".equals(rootNode.get("status").asText())) { JsonNode regeocode = rootNode.get("regeocode"); if (regeocode != null) { return regeocode.get("formatted_address").asText(); } } } } } catch (Exception e) { // 地址解析失败时返回坐标 } return location.toString(); } /** * 构建带参数的URL * @param baseUrl 基础URL * @param params 参数Map * @return 完整的URL */ private String buildUrl (String baseUrl, Map < String, String > params){ StringBuilder urlBuilder = new StringBuilder(baseUrl).append("?"); params.forEach((key, value) -> { urlBuilder.append(key).append("=").append(value).append("&"); }); // 删除最后一个& urlBuilder.deleteCharAt(urlBuilder.length() - 1); return urlBuilder.toString(); } /** * 计算两点间的直线距离(仅供参考,不是实际步行距离) * @param loc1 坐标1 * @param loc2 坐标2 * @return 直线距离(米) */ public static double calculateStraightDistance (Location loc1, Location loc2){ double earthRadius = 6371000; // 地球半径(米) double lat1 = Math.toRadians(loc1.getLatitude()); double lat2 = Math.toRadians(loc2.getLatitude()); double deltaLat = Math.toRadians(loc2.getLatitude() - loc1.getLatitude()); double deltaLon = Math.toRadians(loc2.getLongitude() - loc1.getLongitude()); double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); return earthRadius * c; } }