Найти минимальное расстояние от точки до сложной кривой

У меня сложная кривая, определенная как набор точек в таблице, подобной этой (полная таблица здесь):

#  x   y
1.0577  12.0914
1.0501  11.9946
1.0465  11.9338
...

Если я рисую эту таблицу с помощью команд:

plt.plot(x_data, y_data, c='b',lw=1.)
plt.scatter(x_data, y_data, marker='o', color='k', s=10, lw=0.2)

Я получаю следующее:

enter image description here

где я добавил красные точки и сегменты вручную. Мне нужен способ вычислить эти сегменты для каждой из этих точек, то есть: найти минимальное расстояние от данной точки в этом двумерном пространстве до интерполированной кривой.

Я не могу использовать расстояние до самих точек данных (черные точки, которые генерируют голубую кривую), поскольку они не расположены с равными интервалами, иногда они близки, а иногда они далеко друг от друга, и это сильно влияет на мои результаты далее вниз по линии.

Так как это не хорошо проведенная кривая, я не совсем уверен, что я могу сделать. Я пробовал интерполировать его с помощью UnivariateSpline, но он очень плохо подходит:

# Sort data according to x.
temp_data = zip(x_data, y_data)
temp_data.sort()
# Unpack sorted data.
x_sorted, y_sorted = zip(*temp_data)

# Generate univariate spline.
s = UnivariateSpline(x_sorted, y_sorted, k=5)
xspl = np.linspace(0.8, 1.1, 100)
yspl = s(xspl)

# Plot.
plt.scatter(xspl, yspl, marker='o', color='r', s=10, lw=0.2)

enter image description here

Я также попытался увеличить количество интерполяционных точек, но получил беспорядок:

# Sort data according to x.
temp_data = zip(x_data, y_data)
temp_data.sort()
# Unpack sorted data.
x_sorted, y_sorted = zip(*temp_data)

t = np.linspace(0, 1, len(x_sorted))
t2 = np.linspace(0, 1, 100)    
# One-dimensional linear interpolation.
x2 = np.interp(t2, t, x_sorted)
y2 = np.interp(t2, t, y_sorted)
plt.scatter(x2, y2, marker='o', color='r', s=10, lw=0.2)

enter image description here

Приветствуются любые идеи/указатели.

Ответы

Ответ 1

Если вы открыты для использования библиотеки для этого, посмотрите shapely: https://github.com/Toblerity/Shapely

В качестве быстрого примера (points.txt содержит данные, с которыми вы связались в своем вопросе):

import shapely.geometry as geom
import numpy as np

coords = np.loadtxt('points.txt')

line = geom.LineString(coords)
point = geom.Point(0.8, 10.5)

# Note that "line.distance(point)" would be identical
print point.distance(line)

В качестве интерактивного примера (это также привлекает сегменты линии, которые вы хотели):

import numpy as np
import shapely.geometry as geom
import matplotlib.pyplot as plt

class NearestPoint(object):
    def __init__(self, line, ax):
        self.line = line
        self.ax = ax
        ax.figure.canvas.mpl_connect('button_press_event', self)

    def __call__(self, event):
        x, y = event.xdata, event.ydata
        point = geom.Point(x, y)
        distance = self.line.distance(point)
        self.draw_segment(point)
        print 'Distance to line:', distance

    def draw_segment(self, point):
        point_on_line = line.interpolate(line.project(point))
        self.ax.plot([point.x, point_on_line.x], [point.y, point_on_line.y], 
                     color='red', marker='o', scalex=False, scaley=False)
        fig.canvas.draw()

if __name__ == '__main__':
    coords = np.loadtxt('points.txt')

    line = geom.LineString(coords)

    fig, ax = plt.subplots()
    ax.plot(*coords.T)
    ax.axis('equal')
    NearestPoint(line, ax)
    plt.show()

enter image description here

Заметьте, что я добавил ax.axis('equal'). shapely работает в системе координат, в которой находятся данные. Без графика одинарной оси изображение будет искажено, и пока shapely будет находить ближайшую точку, на дисплее он не будет выглядеть совершенно правильно:

enter image description here

Ответ 2

Кривая по параметрической природе, т.е. для каждого х нет необходимости в единственном у и наоборот. Поэтому вам не следует интерполировать функцию вида y (x) или x (y). Вместо этого вы должны сделать две интерполяции, x (t) и y (t), где t - это, например, индекс соответствующей точки.

Затем вы используете scipy.optimize.fminbound, чтобы найти оптимальное t такое, что (x (t) - x0) ^ 2 + (y (t) - y0) ^ 2 является наименьшим, где (x0, y0) являются красными точек в вашей первой фигуре. Для fminsearch вы можете указать границу min/max для t как 1 и len(x_data)

Ответ 3

Вы можете попытаться выполнить вычисление расстояния от точки к строке на инкрементных парах точек на кривой и найти этот минимум. Это приведет к небольшому количеству ошибок от кривой, как показано на рисунке, но она должна быть очень малой, так как точки относительно близки друг к другу.

http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line