鱼C论坛

 找回密码
 立即注册
查看: 3411|回复: 9

[学习笔记] 两种经纬度计算距离的方法,推荐使用二

[复制链接]
发表于 2021-4-2 09:19:05 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 txl1988 于 2021-4-2 09:23 编辑

工作需要,计算经纬度间的距离之前都是用别人的vba,这不咱也认真学了小甲鱼的Python嘛,所以尝试着用Python来完成
csv表结构非常简单,就三列 [Name        Longitude        Latitude ]
用了两种方法,第一种方法,用 for ,数据量非常的时候,计算的速度还能接受,但是1000*1000的数据,就计算不出来了(用了半天没出结果就取消了)
通过学习得到第二种方法,速度真快,还是1000*1000的数据,1分钟不到
那废话不多说,上干活

求大佬指点,怎么样设计(修改)代码,看上去更加专业点呢

方法一:不推荐啊,只做一种失败的思路参考
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Tue Dec 22 10:35:30 2020

  4. @author: Administrator

  5. 多个经纬度距离计算
  6. 1、修改man函数里面,涉及路径和文件名,其他部分不做修改,执行结果的距离单位为 米m
  7. 2、输入文件列名,Name,Longitude,Latitude(Name就是这个经纬度的标识,可以是基站,小区,eci等等)
  8. """

  9. from math import pi, asin, sqrt, sin, cos, pow
  10. import pandas as pd
  11. from pandas import DataFrame
  12. import time


  13. def rad(d):
  14.     # math.pi = 圆周率π
  15.     return d * pi / 180.0

  16. def getDistance(lat1, lng1, lat2, lng2):
  17.     # 计算两个经纬度之间的距离
  18.     # 地球半径 km 6371.393和6378.137,一般取值6371.393
  19.     EARTH_REDIUS = 6371393
  20.     radLat1 = rad(lat1)
  21.     radLat2 = rad(lat2)

  22.     # 计算两个经度/维度之差,调用上边rad(d)函数
  23.     a = rad(lat1) - rad(lat2)
  24.     b = rad(lng1) - rad(lng2)
  25.     s = 2 * asin(sqrt(pow(sin(a/2), 2) + cos(radLat1) * cos(radLat2) * pow(sin(b/2), 2)))
  26.     s = s * EARTH_REDIUS
  27.     # 格式化字符串,保留2位小数
  28. #    s = '%.2f' % s
  29.     return s

  30. def data(l1,l2):
  31.     # 计算所有经纬度之间的距离

  32.     new_df = DataFrame(columns=['Name_1','Latitude_1','Longitude_1',
  33.                                 'Name_2','Latitude_2','Longitude_2',
  34.                                 'Distance_m'])
  35.    
  36.     for a,b in l1.iterrows():
  37.         for c,d in l2.iterrows():
  38.             lat1 = b['Latitude']
  39.             lng1 = b['Longitude']
  40.             name1 = b['Name']
  41.             
  42.             lat2 = d['Latitude']
  43.             lng2 = d['Longitude']
  44.             name2 = d['Name']
  45.             
  46.             # 距离计算
  47.             jl = getDistance(lat1, lng1, lat2, lng2)
  48.             # 创建新表临时保存
  49.             row =  DataFrame([dict(Name_1=name1, Latitude_1=lat1, Longitude_1=lng1,
  50.                                    Name_2=name2, Latitude_2=lat2, Longitude_2=lng2,
  51.                                    Distance_m = jl), ])
  52.             
  53.             
  54. # =============================================================================
  55. #             # 这一步要对row的列名进行排序,否则会有提示信息,强迫症看着不舒服,append 排序是因为非连接轴未对齐,或者添加sort=False参数忽略
  56. #             row = row[['Name_1','Latitude_1','Longitude_1',
  57. #                        'Name_2','Latitude_2','Longitude_2',
  58. #                        'Distance_m']]
  59. # =============================================================================
  60.    
  61.             # append ,ignore_index=True 根据列名字段对齐,然后合并。最后再重新整理一个新的index。
  62.             new_df = new_df.append(row,ignore_index=True,sort=True)
  63.             new_df = new_df.sort_values(by="Distance_m", ascending=True)
  64.             new_df.drop_duplicates(subset=['Name_1'],keep='first',inplace=True)
  65. # =============================================================================
  66. #             # 如果只对小于n的距离,进行保存,需要加个判断条件,例小于3km
  67. #             if row['Distance_m'].values < 44000:
  68. #                 new_df = new_df.append(row,ignore_index=True,sort=True)
  69. #            
  70. # =============================================================================
  71.             
  72.     #默认排序是按照字母升序(a,b,c,d...),此代码是指定排序方式
  73.     new_df=new_df[['Name_1','Latitude_1','Longitude_1',
  74.                    'Name_2','Latitude_2','Longitude_2',
  75.                    'Distance_m']]
  76.    
  77. #    # 取最近距离
  78. #    #https://developer.aliyun.com/article/705184
  79. #    # 按照距离,升序(小到大)
  80. #    new_df = new_df.sort_values(by="Distance_m", ascending=True)
  81. #    # 去重
  82. #    new_df.drop_duplicates(subset=['Name_1'],keep='first',inplace=True)
  83. #   
  84.    
  85.    
  86.     return new_df


  87. def main():
  88.     starttime = time.time()

  89.     # print("两点间的距离为:%.2f km" %jl)
  90.     path_1 = r"C:\Users\Administrator\练习\经纬度距离计算1.csv"
  91.    
  92.     l1 = pd.read_csv(path_1, encoding='gbk', engine='python')
  93.     path_2 = r"C:\Users\Administrator\练习\经纬度距离计算2.csv"
  94.    
  95.     l2 = pd.read_csv(path_2, encoding='gbk', engine='python')

  96.     result = data(l1,l2)
  97.    
  98.     result.to_csv(r'C:\Users\Administrator\练习\经纬度距离结果.csv',index=0,encoding='gbk')

  99.     print("finish")
  100.   
  101. if __name__ == "__main__":
  102.     print('start...')
  103.     main()
复制代码




方法二:强烈推荐哦
  1. # -*- coding: utf-8 -*-
  2. """
  3. Created on Wed Mar 31 16:36:47 2021

  4. @author: Administrator
  5. """

  6. # -*- coding: utf-8 -*-
  7. """
  8. Created on Tue Dec 22 10:35:30 2020

  9. @author: Administrator

  10. 多个经纬度距离计算
  11. 1、修改man函数里面,涉及路径和文件名,其他部分不做修改,执行结果的距离单位为 米m
  12. 2、输入文件列名,Name,Longitude,Latitude(Name就是这个经纬度的标识,可以是基站,小区,eci等等)
  13. """

  14. from math import pi, asin, sqrt, sin, cos, pow
  15. import pandas as pd
  16. from pandas import DataFrame
  17. import numpy as np
  18. import time


  19. def rad(d):
  20.     # math.pi = 圆周率π
  21.     return d * pi / 180.0

  22. def getDistance(lat1, lng1, lat2, lng2):
  23.     # 计算两个经纬度之间的距离
  24.     # 地球半径 km 6371.393和6378.137,一般取值6371.393   
  25.     EARTH_REDIUS = 6371393
  26.     # 上边rad()函数可以转换成 rad = lambda d:d * pi / 180.0
  27.     radLat1 = rad(lat1)
  28.     radLat2 = rad(lat2)

  29.     # 计算两个经度/维度之差,调用上边rad(d)函数
  30.     a = rad(lat1) - rad(lat2)
  31.     b = rad(lng1) - rad(lng2)
  32.     s = 2 * asin(sqrt(pow(sin(a/2), 2) + cos(radLat1) * cos(radLat2) * pow(sin(b/2), 2)))
  33.     s = s * EARTH_REDIUS
  34.     # 格式化字符串,保留2位小数
  35. #    s = '%.2f' % s
  36.     return s

  37. def data(l1,l2):
  38.     # 计算所有经纬度之间的距离
  39.     #构造共同的列,赋予同样的值
  40.     l1['value']=1
  41.     l2['value']=1

  42.     #将两个数据集进行左连接,笛卡尔积
  43.     Crt_prod=pd.merge(l1,l2,how='left',on='value')
  44.     del Crt_prod['value']
  45. #    #可以修改列名
  46. #    a.rename(columns={'A':'a', 'B':'b', 'C':'c'}, inplace = True)
  47.    
  48.     # 调用计算距离的函数,此处体现能力的时候,之前使用两个for循环,数据多时计算不出来,通过查询,此方法大大节约了时间
  49.     Crt_prod['distance']=Crt_prod.apply(lambda ser: getDistance(ser['Latitude_x'], ser['Longitude_x'], ser['Latitude_y'], ser['Longitude_y']),axis=1)

  50. # =============================================================================
  51.     #如果需要得到到与path_2的哪个位置点与path_1每个位置点的距离最近,运行下面的代码
  52.     #按照distance升序排列,按照name_x 去重
  53.     Crt_prod = Crt_prod.sort_values(by="distance", ascending=True)
  54. #    # 去重
  55.     Crt_prod.drop_duplicates(subset=['Name_x'],keep='first',inplace=True)
  56.    
  57. # =============================================================================  
  58.    
  59. ## =============================================================================
  60. #    #如果按照两点的距离,去除超远距离的,执行此处,主要现在的单位是米
  61. #
  62. #    Crt_prod = Crt_prod[Crt_prod['distance'] < 100]
  63. #
  64. #   
  65. ## =============================================================================  

  66.     return Crt_prod


  67. def main():
  68.     starttime = time.time()
  69.     # print("两点间的距离为:%.2f km" %jl)
  70.     path_1 = r"C:\Users\Administrator\练习\经纬度距离计算1.csv"

  71.     l1 = pd.read_csv(path_1, encoding='gbk', engine='python')
  72.     path_2 = r"C:\Users\Administrator\练习\经纬度距离计算2.csv"

  73.     l2 = pd.read_csv(path_2, encoding='gbk', engine='python')

  74.     result = data(l1,l2)
  75.    
  76.     result.to_csv(r'C:\Users\Administrator\练习\经纬度距离结果.csv',index=0,encoding='gbk')
  77.    
  78.     print("finish")
  79.     endtime=time.time()
  80.     cost_time = endtime - starttime
  81.     print('处理完成,程序运行时间: {}秒'.format(float('%.2f' % cost_time)))
  82.   
  83. if __name__ == "__main__":
  84.     print('start...')
  85.     main()
复制代码

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2021-4-2 09:30:35 From FishC Mobile | 显示全部楼层
如果是py3.x的话,第一行代码就可以删除了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-4-2 09:41:41 | 显示全部楼层
wp231957 发表于 2021-4-2 09:30
如果是py3.x的话,第一行代码就可以删除了

感谢大佬点评,我用的Spyder,他自动生成出来的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-4-2 09:48:06 From FishC Mobile | 显示全部楼层
txl1988 发表于 2021-4-2 09:41
感谢大佬点评,我用的Spyder,他自动生成出来的

它计算的应该是直线距离吧
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-4-2 10:25:34 | 显示全部楼层
wp231957 发表于 2021-4-2 09:48
它计算的应该是直线距离吧

嗯,带地球弧度的直线距离
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-4-2 10:29:02 From FishC Mobile | 显示全部楼层
txl1988 发表于 2021-4-2 10:25
嗯,带地球弧度的直线距离

这是什么意思,举个例子,跑道是100米
那么我们通过你这种算法,测出跑道起点和终点gps,那么计算出来的是否是100米
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-4-2 10:50:07 | 显示全部楼层
wp231957 发表于 2021-4-2 10:29
这是什么意思,举个例子,跑道是100米
那么我们通过你这种算法,测出跑道起点和终点gps,那么计算出来的 ...

不能严格意义上说是直线距离
地图是圆的,因引力作用我们都在地球表面,
就是你拿个篮球,从球表面一个点到另外一个点的距离,不能穿过篮球去测量直线距离吧,应该是那个软尺测量表面距离,是一个意思。
因为100m对于地球半径6371km来说非常小了,测量和计算出来的距离,误差都是在可接受的范围内。
如果测量从广州到黑龙江的距离,不可能拿个尺子去测量,只能通过这种计算的方式,来得到距离。
明白?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-4-2 10:54:55 From FishC Mobile | 显示全部楼层
txl1988 发表于 2021-4-2 10:50
不能严格意义上说是直线距离
地图是圆的,因引力作用我们都在地球表面,
就是你拿个篮球,从球表面一个 ...

就相当于拿个尺子,在地球仪上测量,其结果是包含弧度的
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2021-4-2 10:56:19 From FishC Mobile | 显示全部楼层
txl1988 发表于 2021-4-2 10:50
不能严格意义上说是直线距离
地图是圆的,因引力作用我们都在地球表面,
就是你拿个篮球,从球表面一个 ...

但是高德计算出来的应该是直线距离吧,而且中间还包含各种拐点
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2021-4-2 14:03:54 | 显示全部楼层
wp231957 发表于 2021-4-2 10:56
但是高德计算出来的应该是直线距离吧,而且中间还包含各种拐点

他计算的应该是路线,有道路的距离
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-26 09:12

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表