Python实现从给定点按指定角度、距离统计扇形区域红点数量
Python实现经纬度基准扇形区域红点计数
核心需求
- 所有待统计红色点位、参考地标点均携带
(latitude, longitude)经纬度坐标信息 - 以地标点为基准,结合给定的中心角度(参考值85°)、波束宽度(参考值50°)、有效距离范围三个约束,统计落在对应扇形覆盖区域内的红点总数
实现逻辑
要准确完成统计,需要解决两个球面坐标的核心计算问题,避免直接把经纬度当平面坐标计算带来的误差:
- 两点地表距离计算:采用Haversine公式计算球面两点间的实际距离,适配经纬度坐标的距离判定
- 相对方位角计算:以地标点为原点,计算每个红点相对于正北方向的方位角,同时处理扇形跨0°/360°边界的特殊判定场景
完整代码
import math def haversine_distance(lat1: float, lon1: float, lat2: float, lon2: float, unit: str = "m") -> float: """ 计算两个经纬度点之间的地表距离 :param lat1/lon1: 地标点纬度、经度,单位为十进制度 :param lat2/lon2: 红点纬度、经度,单位为十进制度 :param unit: 距离单位,默认m(米),可选km(千米) """ earth_r = 6371000 if unit == "m" else 6371 phi1 = math.radians(lat1) phi2 = math.radians(lat2) delta_phi = math.radians(lat2 - lat1) delta_lambda = math.radians(lon2 - lon1) a = math.sin(delta_phi / 2) ** 2 + math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda / 2) ** 2 c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) return earth_r * c def calc_relative_bearing(lat1: float, lon1: float, lat2: float, lon2: float) -> float: """ 计算红点相对于地标点的方位角,返回值范围0~360°,正北为0°,顺时针递增 """ phi1 = math.radians(lat1) phi2 = math.radians(lat2) delta_lambda = math.radians(lon2 - lon1) y = math.sin(delta_lambda) * math.cos(phi2) x = math.cos(phi1) * math.sin(phi2) - math.sin(phi1) * math.cos(phi2) * math.cos(delta_lambda) theta = math.atan2(y, x) return (math.degrees(theta) + 360) % 360 def count_red_dots_in_sector( placemark: tuple[float, float], red_dots: list[tuple[float, float]], center_angle: float = 85, beam_width: float = 50, min_dist: float = 0, max_dist: float = 1000, dist_unit: str = "m" ) -> int: """ 统计扇形覆盖区域内的红点总数 :param placemark: 参考地标点坐标,格式(纬度, 经度) :param red_dots: 待统计红点坐标列表,每个元素格式(纬度, 经度) :param center_angle: 扇形中心方位角,单位度 :param beam_width: 扇形波束总张开宽度,单位度 :param min_dist: 有效统计最小距离 :param max_dist: 有效统计最大距离 :param dist_unit: 距离单位 """ count = 0 half_beam = beam_width / 2 angle_start = (center_angle - half_beam) % 360 angle_end = (center_angle + half_beam) % 360 p_lat, p_lon = placemark for dot_lat, dot_lon in red_dots: # 距离校验 dist = haversine_distance(p_lat, p_lon, dot_lat, dot_lon, unit=dist_unit) if not (min_dist <= dist <= max_dist): continue # 方位角校验,兼容跨0°边界的场景 bearing = calc_relative_bearing(p_lat, p_lon, dot_lat, dot_lon) if angle_start > angle_end: in_sector = bearing >= angle_start or bearing <= angle_end else: in_sector = angle_start <= bearing <= angle_end if in_sector: count += 1 return count # 调用示例 if __name__ == "__main__": # 示例地标点 placemark = (39.9042, 116.4074) # 示例待统计红点列表 red_dots = [ (39.9052, 116.4084), (39.9031, 116.4102), (39.9067, 116.4021) ] # 统计:中心角85°、波束宽度50°、有效范围0-500米的红点数量 total = count_red_dots_in_sector(placemark, red_dots, max_dist=500) print(f"扇形区域内红点总数:{total}")
使用说明
- 输入经纬度需为十进制度格式,若原始数据为度分秒格式需提前转换
- 方位角默认遵循正北为0°、顺时针递增的通用规则,若业务场景以其他方向为0°基准,提前做角度偏移转换即可
- Haversine公式在几十公里量级的统计场景下精度足够,若需覆盖超大范围统计,可替换为Vincenty公式提升距离计算精度
- 代码默认处理了扇形跨正北(0°/360°)的边界场景,不会出现区间判断漏算问题
内容的提问来源于stack exchange,提问作者OzcanK
相关产品推荐
相关产品推荐

