You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于距离条件对已排序的经纬度(lat/long)数据进行分组?

按经纬度距离分组的实现方案(Oracle SQL + Python)

我来帮你搞定这个按500米距离阈值分组的需求,下面分别用Oracle SQL和Python两种方式实现,完全匹配你的要求:


一、Oracle SQL实现

Oracle自带的空间数据处理功能可以轻松搞定经纬度距离计算,这里我们用SDO_GEOMETRY和SDO_DISTANCE函数来实现分组逻辑:

步骤说明

  1. 先把lat_long字符串拆分为独立的纬度(lat)和经度(long)数值列;
  2. 将每个经纬度转换为WGS84坐标系(SRID=4326)的空间点;
  3. 使用窗口函数和条件判断,动态生成分组ID:当当前点与当前组首点的距离超过500米时,开启新分组并更新组首点。

完整SQL代码

WITH parsed_data AS (
    -- 拆分lat_long为数值类型的纬度和经度
    SELECT 
        lat_long,
        TO_NUMBER(SUBSTR(lat_long, 1, INSTR(lat_long, ',') - 1)) AS lat,
        TO_NUMBER(SUBSTR(lat_long, INSTR(lat_long, ',') + 1)) AS lon,
        ROW_NUMBER() OVER (ORDER BY lat_long) AS rn
    FROM your_table_name  -- 替换成你的实际表名
),
grouped_data AS (
    SELECT 
        lat_long,
        lat,
        lon,
        rn,
        -- 计算当前点与上一个组首点的距离(单位:米)
        SDO_DISTANCE(
            SDO_GEOMETRY('POINT(' || lon || ' ' || lat || ')', 4326),
            LAG(SDO_GEOMETRY('POINT(' || lon || ' ' || lat || ')', 4326), 1, SDO_GEOMETRY('POINT(' || lon || ' ' || lat || ')', 4326)) OVER (
                ORDER BY rn
            ),
            0.005,  -- 精度参数,越小越精确
            'unit=meter'
        ) AS distance_to_prev_first
    FROM parsed_data
),
final_groups AS (
    SELECT 
        *,
        -- 动态生成分组ID:当距离超过500米时,分组ID自增
        SUM(CASE WHEN distance_to_prev_first > 500 THEN 1 ELSE 0 END) OVER (ORDER BY rn) + 1 AS group_id
    FROM grouped_data
)
-- 最终输出,包含组首点信息
SELECT 
    lat_long,
    group_id AS "group",
    FIRST_VALUE(lat_long) OVER (PARTITION BY group_id ORDER BY rn) AS first_lat_long
FROM final_groups
ORDER BY rn;

说明

  • 替换your_table_name为你的实际表名;
  • SDO_DISTANCE的unit=meter参数确保距离计算单位是米;
  • 最终结果会严格按照你的要求,输出每个点所属的分组以及该组的首点经纬度。

二、Python实现

Python可以用两种方式计算距离:一种是借助geopy库的现成方法,另一种是自己实现Haversine公式(无需额外安装库)。

方式1:使用geopy库(简单快捷)

步骤说明

  1. 安装geopy库:pip install geopy;
  2. 解析经纬度数据,遍历每个点,实时判断是否需要开启新分组;
  3. 记录每个点的分组ID和组首点。

代码实现

from geopy.distance import geodesic

# 你的原始经纬度数据
lat_long_list = [
    "9.999,98.655",
    "9.998,98.647",
    "9.998,98.646",
    "9.998,98.614",
    "9.997,98.655",
    "9.997,98.648",
    "9.997,98.647",
    "9.997,98.646",
    "9.996,98.725"
]

# 解析数据为(lat, lon)元组的列表
parsed_points = []
for item in lat_long_list:
    lat_str, lon_str = item.split(',')
    parsed_points.append((float(lat_str), float(lon_str)))

# 分组逻辑处理
result = []
current_group = 1
first_point = parsed_points[0]
first_lat_long = lat_long_list[0]

# 处理第一个点
result.append({
    "lat_long": lat_long_list[0],
    "group": current_group,
    "first_lat_long": first_lat_long
})

# 遍历剩余点
for idx in range(1, len(parsed_points)):
    current_point = parsed_points[idx]
    # 计算当前点与组首点的距离(单位:米)
    distance = geodesic(first_point, current_point).meters
    if distance > 500:
        # 开启新组
        current_group += 1
        first_point = current_point
        first_lat_long = lat_long_list[idx]
    # 添加到结果
    result.append({
        "lat_long": lat_long_list[idx],
        "group": current_group,
        "first_lat_long": first_lat_long
    })

# 格式化输出结果
print(f"{'lat_long':<15} {'group':<6} {'first_lat_long'}")
for item in result:
    print(f"{item['lat_long']:<15} {item['group']:<6} {item['first_lat_long']}")

方式2:实现Haversine公式(无需额外库)

如果你不想安装第三方库,可以自己实现Haversine公式计算两点球面距离:

代码实现

import math

def haversine_distance(lat1, lon1, lat2, lon2):
    """计算两点之间的球面距离(单位:米)"""
    R = 6371000  # 地球半径,单位米
    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 R * c

# 原始数据和解析部分
lat_long_list = [
    "9.999,98.655",
    "9.998,98.647",
    "9.998,98.646",
    "9.998,98.614",
    "9.997,98.655",
    "9.997,98.648",
    "9.997,98.647",
    "9.997,98.646",
    "9.996,98.725"
]

parsed_points = []
for item in lat_long_list:
    lat_str, lon_str = item.split(',')
    parsed_points.append((float(lat_str), float(lon_str)))

# 分组逻辑
result = []
current_group = 1
first_lat, first_lon = parsed_points[0]
first_lat_long = lat_long_list[0]

result.append({
    "lat_long": lat_long_list[0],
    "group": current_group,
    "first_lat_long": first_lat_long
})

for idx in range(1, len(parsed_points)):
    lat, lon = parsed_points[idx]
    distance = haversine_distance(first_lat, first_lon, lat, lon)
    if distance > 500:
        current_group += 1
        first_lat, first_lon = lat, lon
        first_lat_long = lat_long_list[idx]
    result.append({
        "lat_long": lat_long_list[idx],
        "group": current_group,
        "first_lat_long": first_lat_long
    })

# 格式化输出
print(f"{'lat_long':<15} {'group':<6} {'first_lat_long'}")
for item in result:
    print(f"{item['lat_long']:<15} {item['group']:<6} {item['first_lat_long']}")

Python代码输出示例

运行后会输出和你要求格式一致的结果,比如:

lat_long         group  first_lat_long
9.999,98.655     1      9.999,98.655
9.998,98.647     1      9.999,98.655
9.998,98.646     1      9.999,98.655
9.998,98.614     2      9.998,98.614
9.997,98.655     3      9.997,98.655
9.997,98.648     3      9.997,98.655
9.997,98.647     3      9.997,98.655
9.997,98.646     3      9.997,98.655
9.996,98.725     4      9.996,98.725

内容的提问来源于stack exchange,提问作者Tanakorn Taweepoka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:15:15