how to select some lines created by points based on their distances in python

纵饮孤独 提交于 2021-02-11 12:30:14

问题


I have some lines created by connecting points of a regular grid and want to pair the correct lines to create surfces. This is coordinates of my point array:

coord=np.array([[0.,0.,2.], [0.,1.,3.], [0.,2.,2.], [1.,0.,1.], [1.,1.,3.],\
                [1.,2.,1.], [2.,0.,1.], [2.,1.,1.], [3.,0.,1.], [4.,0.,1.]])

Then, I created lines by connecting points. My points are from a regular grid. So, I have two perpendicular sets of lines. I called them blue (vertical) and red (horizontal) lines. To do so:

blue_line=[]
for ind, i in enumerate (range (len(coord)-1)):
        if coord[i][0]==coord[i+1][0]:
            line=[ind, ind+1]
#             line=[x+1 for x in line]
            blue_line.append(line)
threshold_x = 1.5
threshold_y = 1.5
i, j = np.where((coord[:, 1] == coord[:, np.newaxis, 1]) &
    (abs(coord[:, 0] - coord[:, np.newaxis, 0]) < 1.2 * threshold_y))
# Restrict to where i is before j
i, j = i[i < j], j[i < j]
# Combine and print the indices
red_line=np.vstack([i, j]).T
blue_line=np.array(blue_line)
red_line=np.array(red_line)
all_line=np.concatenate((blue_line, red_line), axis=0)

To find the correct lines for creating surfaces, I check the center of each line with the adjacent ones. I start from the first blue line and try if there are other three adjacent lines or not. If I find any line which its center is less than threshold_x and also its x coordinate is different from that line, I will keep it as a pair. Then I continue searching for adjacent lines with this rule. My fig clearly shows it. First blue line is connected by an arrow to the blue line numbered 3 and also red lines numbered 6 and 7. It is not paired with blue line numbered 2 because they have the same x coordinate. I tried the following but it was not successful to do all the things and I coulnot solve it:

ave_x=[]
ave_y=[]
ave_z=[]
for ind, line in enumerate (all_line):
    x = (coord[line][0][0]+coord[line][1][0])/2
    ave_x.append (x)
    y = (coord[line][0][1]+coord[line][1][1])/2
    ave_y.append (y)
    z = (coord[line][0][2]+coord[line][1][2])/2
    ave_z.append (z)
avs=np.concatenate((ave_x, ave_y, ave_z), axis=0)
avs=avs.reshape(-1,len (ave_x))
avs_f=avs.T
blue_red=[len (blue_line), len (red_line)]
avs_split=np.split(avs_f,np.cumsum(blue_red))[:-1] # first array is center of 
# blue lines and second is center of red lines
dists=[]
for data in avs_split:
    for ind, val in enumerate (data):
    if ind < len(data):
        for ind in range (len(data)-1):
            squared_dist = np.sum((data[ind]-data[ind+1])**2, axis=0)
            dists.append (squared_dist)

In fact I expect my code to give me the resulting list as the pairs of the lines the create three surfaces:

[(1, 6, 3, 7), (2, 7, 4, 8), (3, 9, 5, 10)]

At the end, I want to find the number of lines which are not used in creating the surfaces or are used but are closer than a limit the the dashed line in my fig. I have the coordinate of the two points creating that dashed line:

coord_dash=np.array([[2., 2., 2.], [5., 0., 1.]])

adjacency_threshold=2 These line numbers are also:

[4, 10, 5, 11, 12]

In advance I do appreciate any help.


回答1:


I'm not sure my answer is what you are looking for because your question is a bit unclear. To start off I create the blue and red lines as dictionaries, where the keys are the line numbers and the values are tuples with the star and end point numbers. I also create a dictionary all_mid where the key is the line number and the value is a pandas Series with the coordinates of the mid point.

import numpy as np
import pandas as pd

coord = np.array([[0.,0.,2.], [0.,1.,3.], [0.,2.,2.], [1.,0.,1.], [1.,1.,3.],
                  [1.,2.,1.], [2.,0.,1.], [2.,1.,1.], [3.,0.,1.], [4.,0.,1.]])

df = pd.DataFrame(
    data=sorted(coord, key=lambda item: (item[0], item[1], item[2])), 
    columns=['x', 'y', 'z'], 
    index=range(1, len(coord) + 1))

count = 1
blue_line = {}
for start, end in zip(df.index[:-1], df.index[1:]):
    if df.loc[start, 'x'] == df.loc[end, 'x']:
        blue_line[count] = (start, end)
        count += 1

red_line = []
index = df.sort_values('y').index
for start, end in zip(index[:-1], index[1:]):
    if df.loc[start, 'y'] == df.loc[end, 'y']:
        red_line.append((start, end))
red_line = {i + count: (start, end) 
            for i, (start, end) in enumerate(sorted(red_line))}


all_line = {**blue_line, **red_line}
all_mid = {i: (df.loc[start] + df.loc[end])/2 
           for i, (start, end) in all_line.items()}

The lines look like this:

In [875]: blue_line
Out[875]: {1: (1, 2), 2: (2, 3), 3: (4, 5), 4: (5, 6), 5: (7, 8)}

In [876]: red_line
Out[876]: 
{6: (1, 4),
 7: (2, 5),
 8: (3, 6),
 9: (4, 7),
 10: (5, 8),
 11: (7, 9),
 12: (9, 10)}

Then I define some utility functions:

  • adjacent returns True if the input points are adjacent.
  • left_to_right returns True if the x coordinate of the first point is less than the x coordinate of the second point.
  • connections returns a dictionary in which the key is a line number and the value is a list with the line numbers connected to it.
def adjacent(p, q, threshold=1):
    dx = abs(p['x'] - q['x'])
    dy = abs(p['y'] - q['y'])
    dxy = np.sqrt(dx**2 + dy**2)
    return np.max([dx, dy, dxy]) <= threshold


def left_to_right(p, q):
    return p['x'] < q['x']


def connections(midpoints, it):
    mapping = {}
    for start, end in it:
        if adjacent(midpoints[start], midpoints[end]):
            if left_to_right(midpoints[start], midpoints[end]):
                if start in mapping:
                    if end not in mapping[start]:
                        mapping[start].append(end)
                else:
                    mapping[start] = [end]
    return mapping

We are now ready to create a list of lists, in which each sublist has the line numbers that make up a surface:

from itertools import product, combinations

blues = blue_line.keys()
reds = red_line.keys()

blue_to_red = connections(all_mid, product(blues, reds))
blue_to_blue = connections(all_mid, combinations(blues, r=2))

surfaces = []
for start in blue_line:
    red_ends = blue_to_red.get(start, [])
    blue_ends = blue_to_blue.get(start, [])
    if len(red_ends) == 2 and len(blue_ends) == 1:
        surfaces.append(sorted([start] + red_ends + blue_ends))

This is what you get:

In [879]: surfaces
Out[879]: [[1, 3, 6, 7], [2, 4, 7, 8], [3, 5, 9, 10]]


来源:https://stackoverflow.com/questions/65626486/how-to-select-some-lines-created-by-points-based-on-their-distances-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!