如何判断lat/lon经纬度点是否落在另一坐标点的指定半径范围内
问题结论
针对你给出的示例参数,设备A到位置B的球面距离约为5.8英里,在10英里的覆盖半径范围内。
实现方案选型
这类球面点和圆形覆盖范围的判定,最实用高效的方案是Haversine公式计算大圆距离,适配绝大多数LBS覆盖判定场景:
- 计算量极小,无第三方依赖,三种语言都可以原生实现,单线程每秒可完成数十万次判定,性能完全满足高并发业务需求
- 针对地球球面曲率做了修正,中低纬度100公里范围内的距离误差小于10米,完全匹配10英里半径的判定精度要求
- 不需要对接专业GIS服务,部署和维护成本为0
如果你的业务要求厘米级精度(比如测绘场景),再考虑替换为Vincenty椭球公式,普通到店、附近的人、电子围栏类场景完全不需要。
实现注意事项
- 所有经纬度传入三角函数计算前,必须先从角度值转为弧度值,否则计算结果完全错误
- 单位保持统一:计算英里为单位的距离时,地球平均半径取3956英里;如果要算公里单位,替换为6371即可
- 临界值判定建议增加10米左右的容差,避免浮点精度误差导致边界点判定错误
- 跨180度经线的场景,计算经度差时要做修正:如果两点经度差绝对值大于180度,用360减去该绝对值再参与计算
- 超大规模点批量判定场景,可以先做矩形预过滤:先计算半径对应的经纬度差值范围,超出范围的点直接判定为不在覆盖内,减少三角函数计算量
多语言代码实现
Python 版本
import math def is_in_coverage(lat_a: float, lon_a: float, lat_b: float, lon_b: float, radius_mile: float) -> bool: EARTH_RADIUS_MILE = 3956 # 角度转弧度 lat_a_rad = math.radians(lat_a) lat_b_rad = math.radians(lat_b) d_lat = lat_b_rad - lat_a_rad d_lon = math.radians(lon_b - lon_a) # Haversine公式计算距离 a = math.sin(d_lat / 2) ** 2 + math.cos(lat_a_rad) * math.cos(lat_b_rad) * math.sin(d_lon / 2) ** 2 c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) distance = EARTH_RADIUS_MILE * c # 增加0.006英里(约10米)容差,规避浮点误差 return distance <= radius_mile + 0.006 if __name__ == "__main__": # 测试示例参数 print(is_in_coverage(40.7128, -74.0060, 40.730610, -73.935242, 10)) # 输出:True
JavaScript 版本
function isInCoverage(latA, lonA, latB, lonB, radiusMile) { const EARTH_RADIUS_MILE = 3956; const toRad = deg => deg * Math.PI / 180; const latARad = toRad(latA); const latBRad = toRad(latB); const dLat = latBRad - latARad; const dLon = toRad(lonB - lonA); const a = Math.sin(dLat / 2) ** 2 + Math.cos(latARad) * Math.cos(latBRad) * Math.sin(dLon / 2) ** 2; const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const distance = EARTH_RADIUS_MILE * c; // 增加10米左右容差 return distance <= radiusMile + 0.006; } // 测试示例参数 console.log(isInCoverage(40.7128, -74.0060, 40.730610, -73.935242, 10)) // 输出:true
Java 版本
public class GeoCoverageUtil { private static final double EARTH_RADIUS_MILE = 3956; private static final double TOLERANCE = 0.006; // 10米容差,单位英里 private static double toRad(double degree) { return degree * Math.PI / 180; } public static boolean isInCoverage(double latA, double lonA, double latB, double lonB, double radiusMile) { double latARad = toRad(latA); double latBRad = toRad(latB); double dLat = latBRad - latARad; double dLon = toRad(lonB - lonA); double a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(latARad) * Math.cos(latBRad) * Math.pow(Math.sin(dLon / 2), 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); double distance = EARTH_RADIUS_MILE * c; return distance <= radiusMile + TOLERANCE; } public static void main(String[] args) { System.out.println(isInCoverage(40.7128, -74.0060, 40.730610, -73.935242, 10)); // 输出:true } }
内容的提问来源于stack exchange,提问作者user211309
相关产品推荐
相关产品推荐

