Создайте линейную сеть из ближайших точек с границами

У меня есть набор точек, и я хочу создать сеть линий/дорог из этих точек. Во-первых, мне нужно определить ближайшую точку из каждой из точек. Для этого я использовал дерево KD и разработал такой код:

def closestPoint(source, X = None, Y = None):

df = pd.DataFrame(source).copy(deep = True) #Ensure source is a dataframe, working on a copy to keep the datasource

if(X is None and Y is None):
    raise ValueError ("Please specify coordinate")
elif(not X in df.keys() and not Y in df.keys()):
    raise ValueError ("X and/or Y is/are not in column names")
else:
    df["coord"] = tuple(zip(df[X],df[Y])) #create a coordinate

if (df["coord"].duplicated):
    uniq = df.drop_duplicates("coord")["coord"]
    uniqval = list(uniq.get_values())
    dupl = df[df["coord"].duplicated()]["coord"]
    duplval = list(dupl.get_values())

    for kq,vq in uniq.items():
        clstu = spatial.KDTree(uniqval).query(vq, k = 3)[1]
        df.at[kq,"coord"] = [vq,uniqval[clstu[1]]]
        if([uniqval[clstu[1]],vq] in list(df["coord"]) ):
            df.at[kq,"coord"] = [vq,uniqval[clstu[2]]]

    for kd,vd in dupl.items():
        clstd = spatial.KDTree(duplval).query(vd,k = 1)[1]
        df.at[kd,"coord"] = [vd,duplval[clstd]]
else:
    val = df["coord"].get_values()
    for k,v in df["coord"].items():
        clst = spatial.KDTree(val).query(vd, k = 3)[1]
        df.at[k,"coord"] = [v,val[clst[1]]]
        if([val[clst[1]],v] in list (df["coord"])):
            df.at[k,"coord"] = [v,val[clst[2]]]

return df["coord"]

Код может возвращать ближайшие точки вокруг. Однако мне нужно убедиться, что не создаются двойные линии (например, от (x, y) до (x1, y1) и (x1, y1) до (x, y)), а также мне нужно убедиться, что каждая точка может быть только используется в качестве начальной точки линии и конечной точки линии, несмотря на то, что точка является ближайшей к другим точкам.

Ниже представлена ​​визуализация результата: Результат кода

Чего я хочу: Что я хочу

Я также попытался разделить исходную и целевую координаты и сделать это следующим образом:

df["coord"] = tuple(zip(df[X],df[Y])) #create a coordinate
df["target"] = "" #create a column for target points

count = 2 # create a count iteration
if (df["coord"].duplicated):
  uniq = df.drop_duplicates("coord")["coord"]
  uniqval = list(uniq.get_values())
  for kq,vq in uniq.items():
    clstu = spatial.KDTree(uniqval).query(vq, k = count)[1]
    while not vq in (list(df["target"]) and list(df["coord"])):
        clstu = spatial.KDTree(uniqval).query(vq, k = count)[1]
        df.set_value(kq, "target", uniqval[clstu[count-1]])
    else:
        count += 1
        clstu = spatial.KDTree(uniqval).query(vq, k = count)[1]
        df.set_value(kq, "target", uniqval[clstu[count-1]])

но это возвращает ошибку

IndexError: list index out of range

Кто-нибудь может мне с этим помочь? Большое спасибо!


person botibo    schedule 20.11.2018    source источник


Ответы (2)


Отвечая теперь о глобальной стратегии, вот что я бы сделал (грубый псевдоалгоритм):

current_point = one starting point in uniqval
while (uniqval not empty)
  construct KDTree from uniqval and use it for next line
  next_point = point in uniqval closest to current_point
  record next_point as target for current_point
  remove current_point from uniqval
  current_point = next_point

То, что вы получите, представляет собой линейный график, соединяющий все ваши точки с использованием ближайших соседей «каким-то образом». Не знаю, подойдет ли он для ваших нужд. Вы также получите линейный график, взяв next_point случайным образом...

person Rolvernew    schedule 20.11.2018
comment
Привет! Спасибо за решения! Я пытался сделать это с помощью len(uniqval) != 1, и если нет, то последняя точка будет соединена с первой точкой. Но столкнулся с другой проблемой, хочу записать целевую точку в основной датафрейм (df) с соответствующим индексом текущей точки. Но когда текущая точка меняется на ближайшую точку, как мне ее добавить? - person botibo; 21.11.2018
comment
Когда я пишу запись next_point как цель для current_point, next_point является целью, а current_point все еще доступна (с ее индексом). Кстати, если мои ответы помогут, не стесняйтесь голосовать за них или подтверждать их... - person Rolvernew; 21.11.2018
comment
Ах, я понимаю, что вы имеете в виду, спасибо за ответ! Это то, что я ищу, и я абсолютно проголосую за это. - person botibo; 21.11.2018

Трудно комментировать вашу глобальную стратегию без подробностей о том, какую дорожную сеть вы хотите получить. Итак, позвольте мне просто прокомментировать ваш конкретный код и объяснить, почему возникает ошибка «вне диапазона». Я надеюсь, что это может помочь.

Во-первых, знаете ли вы, что (list_a и list_b) вернет list_a, если он пуст, иначе list_b? Во-вторых, разве условие (vq в list(df["coord"]) не всегда истинно? Если да, то ваш цикл while просто всегда выполняет оператор else, а на последней итерации цикла for (count- 1) будет больше, чем общее количество (уникальных) баллов, поэтому ваш запрос KDTree не возвращает достаточное количество баллов, а clstu[count-1] выходит за пределы допустимого диапазона.

person Rolvernew    schedule 20.11.2018
comment
Спасибо за комментарий! Я действительно смог это представить, и это результат кода i.stack.imgur.com /CWltU.png, тогда как результат, который мне нужен, выглядит следующим образом: i.stack.imgur.com/AxxKN.png. Да, я думаю, что знаю, потому что я хочу сначала заполнить список a, и если точка уже существует в списке a, она должна перейти к list_b (CMIIW). Я думаю, вы правы, тем самым я просто хотел сказать, что если точка использовалась дважды (как начальная точка и целевая точка), то в целевом столбце необходимо добавить еще одну точку на ближайшем расстоянии от повторяемой точки. - person botibo; 20.11.2018
comment
Чтобы быть более конкретным: я думаю, что условие vq in (list(df["target"]) and list(df["coord"])) в вашем коде всегда истинно, поэтому ваш код, вероятно, не делает то, что вы думаете. - person Rolvernew; 20.11.2018
comment
Спасибо за отзыв! Я пытаюсь пройти через это, но все еще застреваю, и все равно он возвращается за пределы досягаемости. У вас есть идеи, как это сделать? - person botibo; 20.11.2018