Java根据经纬度获取两点之间的距离,最近在实现类似于钉钉打卡签到的需求,因为对精度要求不是很高,所以可以通过一个球面距离的公式来求两点距离,这里将地球当成一个球体,实际上地球是一个不规则的球体,所以这个实现方法只能适用一些精度要求不高的需求,如果要高精度,可以用第三方的api去实现。
实现思路
- 先新增一个配置页面,调用百度地图,保存好经纬度数据到数据库表,同时也保存距离
- 手机打卡获取当前位置的经纬度数据,通过接口对比,计算两点距离是否在配置的打卡范围内
代码实现
写一个实体类,传入经纬度信息
package cn.nzp.ems.ecb.server.common;
import lombok.Data;
@Data
public class PositionParam {
private String positionlng; //坐标经度
private String positionlat; //坐标纬度
}
写一个工具类,EARTH_RADII
为地球半径的估值
package cn.server.common;
public class GPSUtil {
// 圆周率
private static final Double PI = Math.PI;
private static final Double PK = 180 / PI;
private static final Double EARTH_RADII = 6370996.81;
/**
* 计算两个百度地图坐标实际距离:米
*
* @param sourcePosition
* @param targetPosition
* @return
*/
public static double getDistanceByCoordinate(PositionParam sourcePosition, PositionParam targetPosition) {
double o_lat = Double.valueOf(sourcePosition.getPositionlat());
double o_lng = Double.valueOf(sourcePosition.getPositionlng());
double d_lat = Double.valueOf(targetPosition.getPositionlat());
double d_lng = Double.valueOf(targetPosition.getPositionlng());
double t1 = Math.cos(o_lat / PK) * Math.cos(o_lng / PK) * Math.cos(d_lat / PK) * Math.cos(d_lng / PK);
double t2 = Math.cos(o_lat / PK) * Math.sin(o_lng / PK) * Math.cos(d_lat / PK) * Math.sin(d_lng / PK);
double t3 = Math.sin(o_lat / PK) * Math.sin(d_lat / PK);
double tt = Math.acos(t1 + t2 + t3);
return EARTH_RADII * tt;
}
}
接口判断是否在签到范围内
public ResultResponse<Integer> checkInGpsRange(QueryDto queryDto) {
ResultResponse<Integer> resultResponse = ResultResponse.getSuccessfulResultResponse(AppConsts.NO_IN_GPS_RANGE);
// 获取配置gps签到范围数据
List<GpsAddress> gpsAddressList = Optional.ofNullable(gpsAddressMapper.listGpsAddressByConfigId(queryDto.getId())).orElse(Lists.newArrayList());
for (GpsAddress gpsAddress : gpsAddressList) {
if (Objects.isNull(gpsAddress.getRangeNum())) {
continue;
}
// 主要复制经纬度信息,sourceParam是手机打卡时候,前端传过来的
PositionParam sourceParam = BeanUtil.copyProperties(queryDto, PositionParam.class);
// 这个是数据库保存的打卡范围,经纬度信息
PositionParam targetParam = BeanUtil.copyProperties(gpsAddress, PositionParam.class);
// 判断是否在签到范围内
double difference = GPSUtil.getDistanceByCoordinate(sourceParam, targetParam);
// 在签到范围内
if (difference < gpsAddress.getRangeNum()) {
resultResponse.setData(AppConsts.IN_GPS_RANGE);
return resultResponse;
}
}
return resultResponse;
}
ps,这个是通过Java实现的简单例子,只能适用于不是特别精准的情况,要特别精准,请用第三方api,比如百度的,https://lbsyun.baidu.com/