Windows环境Docker部署OSRM下,Python实现新增节点时快速更新距离矩阵的最优方案
嘿,作为Python新手能折腾OSRM和距离矩阵已经超棒啦!先给你点个赞😉
首先得说,你之前的代码效率低的核心原因是逐个调用OSRM的directions接口——300×300就是90000次请求,每次请求都有网络延迟,400秒其实已经不算慢了。不过咱们可以用OSRM专门的Table API来解决这个问题,不管是初始计算还是新增节点,效率都会提升几个数量级!
先把初始计算的效率提上去(顺便为新增节点铺路)
OSRM的Table API就是专门用来批量生成距离/时间矩阵的,一次请求就能搞定所有点对,不用循环90000次。你的初始代码可以改成这样:
from routingpy import OSRM from routingpy.routers import options import numpy as np # 初始化OSRM客户端 options.default_timeout = None client = OSRM(base_url="你的OSRM服务地址") # 把经纬度整理成 [(经度, 纬度), ...] 的格式 existing_locations = list(zip(locations_DCA_df['Longitude'], locations_DCA_df['Latitude'])) # 一次请求获取完整的300×300距离矩阵 table_result = client.table(locations=existing_locations, profile='car') # 转成numpy矩阵,方便后续操作 distance_matrix = np.array(table_result.distances)
这样初始计算可能只需要几秒,而不是400秒!记得把这个矩阵存到本地,比如用np.save("distance_matrix_300x300.npy", distance_matrix),下次直接加载就行。
新增节点的最快方法:只算必要的点对
当你新增一个节点时,完全不需要重新计算整个301×301矩阵,只需要算新节点和所有原有节点的双向距离,然后把这些距离插入到原矩阵里就行。具体步骤如下:
1. 准备新节点的位置
假设新节点的经纬度是new_lon和new_lat:
new_location = (new_lon, new_lat)
2. 批量计算双向距离(用Table API,一次请求搞定一组)
# 计算新节点到所有原有节点的距离 new_to_existing = client.table( locations=[new_location], # 起点:新节点 destinations=existing_locations, # 终点:所有原有节点 profile='car' ).distances[0] # 取第一个结果,因为只有一个起点 # 计算所有原有节点到新节点的距离 existing_to_new = client.table( locations=existing_locations, # 起点:所有原有节点 destinations=[new_location], # 终点:新节点 profile='car' ).distances # 把结果扁平化(从300×1变成一维列表) existing_to_new = [d[0] for d in existing_to_new]
小优化:如果你的场景里没有单行道(或者可以忽略单行道),那么
new_to_existing和existing_to_new是完全一样的,这时候可以只算一次,省一个请求~
3. 把新距离插入原矩阵
现在把原300×300矩阵扩展成301×301:
# 第一步:给原矩阵的每一行末尾添加「原有节点到新节点」的距离 extended_matrix = np.hstack([distance_matrix, np.array(existing_to_new).reshape(-1, 1)]) # 第二步:新增一行,内容是「新节点到所有原有节点的距离」+ [0](新节点到自己的距离为0) new_row = np.array(new_to_existing + [0]).reshape(1, -1) # 第三步:把新行拼接到扩展后的矩阵下面 final_matrix = np.vstack([extended_matrix, new_row])
4. 保存新矩阵
别忘了把更新后的矩阵存起来,下次新增节点直接用这个:
np.save("distance_matrix_301x301.npy", final_matrix)
最后再提个小建议
如果你后续要频繁新增节点,最好把existing_locations也存到本地(比如用pickle或者csv),这样每次新增时不用再从DataFrame里读,直接加载就行,更省时间~
内容的提问来源于stack exchange,提问作者mj_davis

