在另一条线的一点处画固定长度的垂直线

问题描述 投票:0回答:5

我有两个点 A (10,20) 和 B (15,30)。这些点生成一条线 AB。我需要在Python中在B点上画一条长度为6(每个方向3个单位)的垂线CD。

我已经使用以下代码获得了 AB 线的一些属性:

from scipy import stats
x = [10,15]
y = [20,30]
slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)

如何计算C和D的位置。我需要它们的X和Y值。 enter image description here

C 和 D 的值将用于使用 Shapely 库实现另一个目标。

python geometry shapely
5个回答
19
投票

既然你对使用 Shapely 感兴趣,我能想到的获得垂直线的最简单方法是使用

parallel_offset
方法获得两条平行线到 AB,并连接它们的端点:

from shapely.geometry import LineString

a = (10, 20)
b = (15, 30)
cd_length = 6

ab = LineString([a, b])
left = ab.parallel_offset(cd_length / 2, 'left')
right = ab.parallel_offset(cd_length / 2, 'right')
c = left.boundary[1]
d = right.boundary[0]  # note the different orientation for right offset
cd = LineString([c, d])

enter image description here

以及CD的坐标:

>>> c.x, c.y
(12.316718427000252, 31.341640786499873)
>>> d.x, d.y
(17.683281572999746, 28.658359213500127)

7
投票

如果AB的斜率是

slope
,那么CD的斜率就是
-1/slope
。这等于垂直变化对水平变化的影响:
dy/dx = -1/slope
。这给出了
dx = -slope*dx
。根据毕达哥拉斯定理,你有
3**2 = dy**2+dx**2
。替换
dx
,你得到

3**2 = (-slope*dy)**2+dy**2

3**2 = (slope**2 + 1)*dy**2

dy**2 = 3**2/(slope**2+1)

dy = math.sqrt(3**2/(slope**2+1))

然后你就可以得到

dx = -slope*dy
。最后,你可以使用
dx
dy
来得到 C 和 D。所以代码是:

import math
dy = math.sqrt(3**2/(slope**2+1))
dx = -slope*dy
C[0] = B[0] + dx
C[1] = B[1] + dy
D[0] = B[0] - dx
D[1] = B[1] - dy

(注意虽然

math.sqrt
只返回一个数字,但一般来说是有正负平方根的。C对应正平方根,D对应负平方根)。


2
投票

您可能应该使用向量来计算点的位置。

  • 创建
    vector AB
  • 计算它的
    normalized perpendicular
  • 将此值加或减 3 倍即可
    B

借助简单、可重复使用的

Vector class
,计算变得微不足道,读起来像英语:

找到与点

AB
距离
3
垂直于
B
的点:
P1 = B + (B-A).perp().normalized() * 3
P2 = B + (B-A).perp().normalized() * 3

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)
    def dot(self, other):
        return self.x * other.x + self.y * other.y
    def norm(self):
        return self.dot(self)**0.5
    def normalized(self):
        norm = self.norm()
        return Vector(self.x / norm, self.y / norm)
    def perp(self):
        return Vector(1, -self.x / self.y)
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)
    def __str__(self):
        return f'({self.x}, {self.y})'


A = Vector(10, 20)
B = Vector(15, 30)

AB = B - A  
AB_perp_normed = AB.perp().normalized()
P1 = B + AB_perp_normed * 3
P2 = B - AB_perp_normed * 3

print(f'Point{P1}, and Point{P2}')

输出:

Point(17.683281572999746, 28.658359213500127), and Point(12.316718427000252, 31.341640786499873)

0
投票

如果我们有一条平行于x轴或y轴的线,那么我们可以将以下两个方法添加到Vector类中。

def perptoy(self):
    return Vector(1, 0)
def perptox(self):
    return Vector(1, -10000) // any big number will do instead of -inf

AB = B - A  
if (A.y == B.y):
   AB_perp_normed = AB.perptox().normalized();
elif (A.x == B.x):
   AB_perp_normed = AB.perptoy().normalized();
else:
   AB_perp_normed = AB.perp().normalized();

其余部分与之前的帖子相同。


0
投票

我的版本如下

import math
def get_perp_coords(left_x, left_y, right_x, right_y, length, divider):
    x_length = right_x - left_x
    y_length = right_y - left_y

    mag = math.sqrt(x_length**2 + y_length**2)    
    x_length_norm = x_length / mag
    y_length_norm = y_length / mag
    
    # middle point
    middle_x = round((left_x + right_x) / 2)
    middle_y = round((left_y + right_y) / 2)
    
    # switch
    temp = x_length_norm
    x_length_norm = -1 * y_length_norm
    y_length_norm = temp
    
    # get coords
    ratio = 1 / divider
    new_x_1 = middle_x + x_length_norm * length * ratio
    new_y_1 = middle_y + y_length_norm * length * ratio
    new_x_2 = middle_x - x_length_norm * length * (1 - ratio)
    new_y_2 = middle_y - y_length_norm * length * (1 - ratio)
    
    # if coords goes beyond the original coordinate system
    if new_x_1 < 0 or new_y_1 < 0 or new_x_2 < 0 or new_y_2 < 0:
        ratio = 1 - ratio
        new_x_1 = middle_x + x_length_norm * length * ratio
        new_y_1 = middle_y + y_length_norm * length * ratio
        new_x_2 = middle_x - x_length_norm * length * (1 - ratio)
        new_y_2 = middle_y - y_length_norm * length * (1 - ratio)
        
    return int(new_x_1), int(new_y_1), int(new_x_2), int(new_y_2)
  • 此代码假设从中间绘制一条垂直线。如果没有,可以用想要的坐标修改该部分。
  • length
    是设置线的长度
  • divider
    是设置垂直线上下部分的比例,防止垂直线越过正值坐标系
© www.soinside.com 2019 - 2024. All rights reserved.