Python 多个2D点之间的最短路径(Shapely LineString内的旅行商?)

Python 多个2D点之间的最短路径(Shapely LineString内的旅行商?),python,shapely,geopandas,multilinestring,Python,Shapely,Geopandas,Multilinestring,我试图根据点地形测量创建河流横断面图。当尝试从一系列具有公共id的点创建一个形状优美的LineString时,我意识到给定点的顺序确实很重要,因为LineString只会“按索引”连接给定点(按给定顺序连接列表中的点)。下面的代码说明了默认行为: from shapely.geometry import Point, LineString import geopandas as gpd import numpy as np import matplotlib.pyplot as plt # G

我试图根据点地形测量创建河流横断面图。当尝试从一系列具有公共id的点创建一个形状优美的
LineString
时,我意识到给定点的顺序确实很重要,因为
LineString
只会“按索引”连接给定点(按给定顺序连接列表中的点)。下面的代码说明了默认行为:

from shapely.geometry import Point, LineString
import geopandas as gpd
import numpy as np
import matplotlib.pyplot as plt

# Generate random points
x=np.random.randint(0,100,10)
y=np.random.randint(0,50,10)
data = zip(x,y)

# Create Point and default LineString GeoSeries
gdf_point = gpd.GeoSeries([Point(j,k) for j,k in data])
gdf_line = gpd.GeoSeries(LineString(zip(x,y)))

# plot the points and "default" LineString
ax = gdf_line.plot(color='red')
gdf_point.plot(marker='*', color='green', markersize=5,ax=ax)
这将产生这样的形象:

问题:在Shapely中是否有任何内置方法可以自动通过给定的随机2D点列表创建最符合逻辑(即:最短、最不复杂、最不交叉……)的线

与默认行(红色)相比,您可以在下面找到所需的行(绿色)


没有内置函数,但shapely有一个函数

您可以轻松地迭代这些点,计算它们之间的最短距离,并构建“最短”路径


在官方的github repo中有一些。以下是解决我的横截面
行字符串
简化问题的方法。然而,我的解决方案没有正确地解决计算上更复杂的任务,即通过给定的点找到最终最短路径。正如评论者所建议的,有很多库和脚本可以用来解决这个特殊的问题,但是如果有人想保持它的简单性,你可以使用为我设计的技巧。请随意使用和评论

def simplify_LineString(linestring):

    '''
    Function reorders LineString vertices in a way that they each vertix is followed by the nearest remaining vertix.
    Caution: This doesn't calculate the shortest possible path (travelling postman problem!) This function performs badly
    on very random points since it doesn't see the bigger picture.
    It is tested only with the positive cartesic coordinates. Feel free to upgrade and share a better function!

    Input must be Shapely LineString and function returns Shapely Linestring.

    '''

    from shapely.geometry import Point, LineString
    import math

    if not isinstance(linestring,LineString):
        raise IOError("Argument must be a LineString object!")

    #create a point lit
    points_list = list(linestring.coords)

    ####
    # DECIDE WHICH POINT TO START WITH - THE WESTMOST OR SOUTHMOST? (IT DEPENDS ON GENERAL DIRECTION OF ALL POINTS)
    ####
    points_we = sorted(points_list, key=lambda x: x[0])
    points_sn = sorted(points_list, key=lambda x: x[1])

    # calculate the the azimuth of general diretction
    westmost_point = points_we[0]
    eastmost_point = points_we[-1]

    deltay = eastmost_point[1] - westmost_point[1]
    deltax = eastmost_point[0] - westmost_point[0]

    alfa = math.degrees(math.atan2(deltay, deltax))
    azimut = (90 - alfa) % 360

    if (azimut > 45 and azimut < 135):
        #General direction is west-east
        points_list = points_we
    else:
        #general direction is south-north
        points_list = points_sn

    ####
    # ITERATIVELY FIND THE NEAREST VERTIX FOR THE EACH REMAINING VERTEX
    ####

    # Create a new, ordered points list, starting with the east or southmost point.
    ordered_points_list = points_list[:1]

    for iteration in range(0, len(points_list[1:])):

        current_point = ordered_points_list[-1]  # current point that we are looking the nearest neighour to
        possible_candidates = [i for i in points_list if i not in ordered_points_list]  # remaining (not yet sortet) points

        distance = 10000000000000000000000
        best_candidate = None
        for candidate in possible_candidates:
            current_distance = Point(current_point).distance(Point(candidate))
            if current_distance < distance:
                best_candidate = candidate
                distance = current_distance

        ordered_points_list.append(best_candidate)

    return LineString(ordered_points_list)
def simplify_LineString(LineString):
'''
函数将线串顶点重新排序,使每个顶点后面紧跟着最近的剩余顶点。
警告:这不会计算最短可能路径(旅行邮递员问题!)此函数性能不佳
在非常随机的点上,因为它看不到更大的画面。
仅使用正笛卡尔坐标对其进行测试。随时升级和共享更好的功能!
输入必须是Shapely LineString,函数返回Shapely LineString。
'''
从shapely.geometry导入点,LineString
输入数学
如果不是isinstance(linestring,linestring):
raise IOError(“参数必须是LineString对象!”)
#创建点光源
点列表=列表(linestring.coords)
####
#决定从哪一点开始-最西边还是最南边?(取决于所有点的大致方向)
####
点=已排序(点列表,键=λx:x[0])
点编号=已排序(点列表,键=λx:x[1])
#计算总方向的方位角
最西端的点=点[0]
最东点=点[1]
deltay=最东的_点[1]-最西的_点[1]
deltax=最东的_点[0]-最西的_点[0]
阿尔法=数学学位(数学atan2(德尔泰、德尔泰))
方位角=(90-alfa)%360
如果(方位角>45且方位角<135):
#总方向是西-东
积分列表=积分
其他:
#大致方向是南北向
点列表=点编号
####
#迭代查找每个剩余顶点的最近VERTIX
####
#创建一个新的有序点列表,从最东面或最南面的点开始。
有序点列表=点列表[:1]
对于范围(0,len)(点列表[1:])内的迭代:
当前点=有序点列表[-1]#我们正在查找最近邻居的当前点
可能的候选点=[如果我不在有序的点列表中,则我代表点列表中的i]#剩余(尚未排序)点
距离=100000000000000000000
最佳候选人=无
对于可能的_候选人中的候选人:
当前距离=点(当前点)。距离(点(候选))
如果当前距离<距离:
最佳候选人
距离=当前距离
有序点列表。追加(最佳候选点)
返回行字符串(有序点列表)

我自己也做过类似的函数,但它只从最西边或最南边的点开始,然后迭代地为每个剩余点找到最近的邻居。但事实上,这并不意味着所有的路径都是最短的。如果我能想出一个像样的解决方案,我会发布更多。无论如何谢谢你!假设您事先不知道顺序或邻居,您可以尝试构建一个图,将每个节点连接到每个其他节点,然后搜索“简单路径”,选择与节点数相同步数的路径,然后选择其中最短的一个?这需要类似于networkX的东西。哇,看起来很有希望!将对此进行研究。小更正:路径长度将是节点-1如果有序点列表[-1]为无,则添加一个
:有序点列表。最后的pop()
应允许通过
返回类型(linestring)(有序点列表)
处理线性错误。