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<String, String> 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<String, String> 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<String, String> 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;
|
}
|
|
|
}
|