许多2D点之间的最短路径(Shapely LineString中的旅行推销员?)

时间:2017-01-06 10:00:00

标签: python shapely geopandas multilinestring

我试图根据点测量结果创建河流横截面剖面。当尝试从具有公共id的一系列点创建一个Shapely LineString时,我意识到给定点的顺序确实很重要,因为LineString只能连接给定点'indexwise'(按列表给定的顺序连接点)。以下代码说明了默认行为:

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)

这会产生图像:

Default LineString

问题:Shapely中是否有任何内置方法会自动创建最符合逻辑的(又称:最短,最不复杂,最不交叉) ,...)在给定的2D点列表中划线?

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

Desired LineString

2 个答案:

答案 0 :(得分:1)

没有内置功能,但形状上有distance功能。

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

官方github回购邮件中有一些examples

答案 1 :(得分:0)

以下是解决我的横截面LineString简化问题的原因。但是,我的解决方案并没有正确地解决通过给定点找到最终最短路径的计算更复杂的任务。正如评论者所说,有许多库和脚本可以解决这个特殊问题,但是如果有人想保持简单,你可以使用我的技巧。随意使用和评论!

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)