1. 直接用公式计算
2. 使用redis geo计算
3. 调用地图计算【这里选择的是腾讯地图】
1. 使用公式直接计算
//地球半径
private final static double EARTH_RADIUS = 6378.137;
private static double rad(double d) {
return d * Math.PI / 180.0;
}
/**
* 计算坐标系两点间距离
*
* @return double 距离 单位公里,精确到米
*/
public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double a = radLat1 - radLat2;
double b = rad(lng1) - rad(lng2);
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
s = s * EARTH_RADIUS;
s = new BigDecimal(s).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
return s;
}
2. 使用redis geo计算
/**
* @param key
* @param lng1 定位Lon
* @param lat1 定位Lat
* @param lng2 目标Lon
* @param lat2 目标Lat
* @return
*/
public static double getDistance(String key, double lng1, double lat1, double lng2, double lat2) {
String m1 = K1 + lng1 + ":" + lat1;
String m2 = K1 + lng2 + ":" + lat2;
String hashKey = lng1 + ":" + lat1 + "-" + lng2 + ":" + lat2;
try {
// 从缓存取
Double distanceCache = (Double)redisTemplate.opsForHash().get(K2, hashKey);
if (Objects.nonNull(distanceCache)) {
return distanceCache;
}
redisTemplate.opsForGeo().add(key, new Point(lng1, lat1), m1);
try {
// 从redis geo计算
Distance distance = redisTemplate.opsForGeo().distance(key, m1, m2, RedisGeoCommands.DistanceUnit.KILOMETERS);
if (Objects.nonNull(distance)) {
double distanceValue = formatDouble(distance.getValue());
redisTemplate.opsForHash().put(K2, hashKey, distanceValue);
return distanceValue;
}
} catch (Exception e) {
log.error(e.getMessage());
}
// 没有计算结果,加入进去,重新计算
redisTemplate.opsForGeo().add(key, new Point(lng2, lat2), m2);
Distance distance1 = redisTemplate.opsForGeo().distance(key, m1, m2, RedisGeoCommands.DistanceUnit.KILOMETERS);
if (Objects.nonNull(distance1)) {
double distanceValue = formatDouble(distance1.getValue());
redisTemplate.opsForHash().put(K2, hashKey, distanceValue);
return distanceValue;
} else {
// 还是计算失败,传最大值,排在最末
return Double.MAX_VALUE;
}
} catch (Exception e) {
log.error("计算距离失败:", e.getMessage());
return Double.MAX_VALUE;
}
}
/**
* 保留2位小数
* @param initV
* @return
*/
private static double formatDouble(double initV) {
BigDecimal b = new BigDecimal(initV);
return b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
3. 使用腾讯地图精准计算2点步行距离
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Objects;
@Component
@Slf4j
public class TencentMap {
//访问腾讯地图接口KEY
private static String KEY;
private static RestTemplate restTemplate;
private static RedisTemplate redisTemplate;
@Autowired
public void setRedisTemplate(RestTemplate restTemplate, @Value("${tencentMap.key}") String key, RedisTemplate redisTemplate) {
TencentMap.restTemplate = restTemplate;
TencentMap.KEY = key;
TencentMap.redisTemplate = redisTemplate;
}
/**
* 计算腾讯地图距离
* @param max 终点个数
* @param from 起点坐标,格式:lat,lng;lat,lng...
* (经度与纬度用英文逗号分隔,坐标间用英文分号分隔)
* @param to 终点(可以传入多个终点坐标,格式:lat,lng;lat,lng...
* (http://apis.map.qq.com/ws/distance/v1/matrix?mode=walking&from=22.53332,113.93041&to=22.529565,113.946620&key=NNWBZ-VJ4CX-PON4E-TQHCA-ZY5Q7-Z7BE7
* {"status":0,"message":"query ok","request_id":"6801723632908333780","result":{"rows":[{"elements":[{"distance":2496}]}]}}
* @return 距离
*/
public static Double[] getLocation(int max, String from, String to) {
Double[] distances = new Double[max];
//请求路径
String getURL = "http://apis.map.qq.com/ws/distance/v1/matrix";
//计算方式:driving(驾车)、walking(步行)默认:driving
String mode = "walking";
String urlString = getURL + "?mode=" + mode + "&from=" + from + "&to=" + to + "&key=" + KEY;
String result = "";
try {
result = restTemplate.getForObject(urlString, String.class);
} catch (Exception e) {
log.error(e.getMessage());
}
if (!result.contains("result")) {
log.error("调用腾讯地图计算步行距离返回报错,{}", result);
return distances;
}
log.info("调用腾讯地图计算步行距离返回结果:{}", result);
// 转JSON格式
JSONObject jsonObject = JSON.parseObject(result).getJSONObject("result");
//elements是[](数组格式)所以使用JSONArray获取
JSONArray rows = jsonObject.getJSONArray("rows");
// 1对多模式
JSONArray adInfo = rows.getJSONObject(0).getJSONArray("elements");
//for数组
for (int j = 0; j < adInfo.size(); j++) {
JSONObject jsonObject2 = adInfo.getJSONObject(j);
//获取距离(获取到的是m为单位)
String distance = jsonObject2.getString("distance");
double distanceD = Double.parseDouble(distance);
//转化为km
distanceD = distanceD / 1000;
distances[j] = DistanceHelper.formatDouble(distanceD);
}
return distances;
}
}
【腾讯地图国际版当前不支持2点距离计算】
【曲线救国:腾讯地图支持国外2点的路线规划,里面会包含距离。可以拿出来使用】