How do I compute the intersection point of two lines? - python

I have two lines that intersect at a point. I know the endpoints of the two lines. How do I compute the intersection point in Python?
# Given these endpoints
#line 1
A = [X, Y]
B = [X, Y]
#line 2
C = [X, Y]
D = [X, Y]
# Compute this:
point_of_intersection = [X, Y]

Unlike other suggestions, this is short and doesn't use external libraries like numpy. (Not that using other libraries is bad...it's nice not need to, especially for such a simple problem.)
def line_intersection(line1, line2):
xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])
def det(a, b):
return a[0] * b[1] - a[1] * b[0]
div = det(xdiff, ydiff)
if div == 0:
raise Exception('lines do not intersect')
d = (det(*line1), det(*line2))
x = det(d, xdiff) / div
y = det(d, ydiff) / div
return x, y
print line_intersection((A, B), (C, D))
And FYI, I would use tuples instead of lists for your points. E.g.
A = (X, Y)
EDIT: Initially there was a typo. That was fixed Sept 2014 thanks to #zidik.
This is simply the Python transliteration of the following formula, where the lines are (a1, a2) and (b1, b2) and the intersection is p. (If the denominator is zero, the lines have no unique intersection.)

Can't stand aside,
So we have linear system:
A1 * x + B1 * y = C1
A2 * x + B2 * y = C2
let's do it with Cramer's rule, so solution can be found in determinants:
x = Dx/D
y = Dy/D
where D is main determinant of the system:
A1 B1
A2 B2
and Dx and Dy can be found from matricies:
C1 B1
C2 B2
and
A1 C1
A2 C2
(notice, as C column consequently substitues the coef. columns of x and y)
So now the python, for clarity for us, to not mess things up let's do mapping between math and python. We will use array L for storing our coefs A, B, C of the line equations and intestead of pretty x, y we'll have [0], [1], but anyway. Thus, what I wrote above will have the following form further in the code:
for D
L1[0] L1[1]
L2[0] L2[1]
for Dx
L1[2] L1[1]
L2[2] L2[1]
for Dy
L1[0] L1[2]
L2[0] L2[2]
Now go for coding:
line - produces coefs A, B, C of line equation by two points provided,
intersection - finds intersection point (if any) of two lines provided by coefs.
from __future__ import division
def line(p1, p2):
A = (p1[1] - p2[1])
B = (p2[0] - p1[0])
C = (p1[0]*p2[1] - p2[0]*p1[1])
return A, B, -C
def intersection(L1, L2):
D = L1[0] * L2[1] - L1[1] * L2[0]
Dx = L1[2] * L2[1] - L1[1] * L2[2]
Dy = L1[0] * L2[2] - L1[2] * L2[0]
if D != 0:
x = Dx / D
y = Dy / D
return x,y
else:
return False
Usage example:
L1 = line([0,1], [2,3])
L2 = line([2,3], [0,4])
R = intersection(L1, L2)
if R:
print "Intersection detected:", R
else:
print "No single intersection point detected"

Here is a solution using the Shapely library. Shapely is often used for GIS work, but is built to be useful for computational geometry. I changed your inputs from lists to tuples.
Problem
# Given these endpoints
#line 1
A = (X, Y)
B = (X, Y)
#line 2
C = (X, Y)
D = (X, Y)
# Compute this:
point_of_intersection = (X, Y)
Solution
import shapely
from shapely.geometry import LineString, Point
line1 = LineString([A, B])
line2 = LineString([C, D])
int_pt = line1.intersection(line2)
point_of_intersection = int_pt.x, int_pt.y
print(point_of_intersection)

Using formula from:
https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
def findIntersection(x1,y1,x2,y2,x3,y3,x4,y4):
px= ( (x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4) ) / ( (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) )
py= ( (x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4) ) / ( (x1-x2)*(y3-y4)-(y1-y2)*(x3-x4) )
return [px, py]

If your lines are multiple points instead, you can use this version.
import numpy as np
import matplotlib.pyplot as plt
"""
Sukhbinder
5 April 2017
Based on:
"""
def _rect_inter_inner(x1,x2):
n1=x1.shape[0]-1
n2=x2.shape[0]-1
X1=np.c_[x1[:-1],x1[1:]]
X2=np.c_[x2[:-1],x2[1:]]
S1=np.tile(X1.min(axis=1),(n2,1)).T
S2=np.tile(X2.max(axis=1),(n1,1))
S3=np.tile(X1.max(axis=1),(n2,1)).T
S4=np.tile(X2.min(axis=1),(n1,1))
return S1,S2,S3,S4
def _rectangle_intersection_(x1,y1,x2,y2):
S1,S2,S3,S4=_rect_inter_inner(x1,x2)
S5,S6,S7,S8=_rect_inter_inner(y1,y2)
C1=np.less_equal(S1,S2)
C2=np.greater_equal(S3,S4)
C3=np.less_equal(S5,S6)
C4=np.greater_equal(S7,S8)
ii,jj=np.nonzero(C1 & C2 & C3 & C4)
return ii,jj
def intersection(x1,y1,x2,y2):
"""
INTERSECTIONS Intersections of curves.
Computes the (x,y) locations where two curves intersect. The curves
can be broken with NaNs or have vertical segments.
usage:
x,y=intersection(x1,y1,x2,y2)
Example:
a, b = 1, 2
phi = np.linspace(3, 10, 100)
x1 = a*phi - b*np.sin(phi)
y1 = a - b*np.cos(phi)
x2=phi
y2=np.sin(phi)+2
x,y=intersection(x1,y1,x2,y2)
plt.plot(x1,y1,c='r')
plt.plot(x2,y2,c='g')
plt.plot(x,y,'*k')
plt.show()
"""
ii,jj=_rectangle_intersection_(x1,y1,x2,y2)
n=len(ii)
dxy1=np.diff(np.c_[x1,y1],axis=0)
dxy2=np.diff(np.c_[x2,y2],axis=0)
T=np.zeros((4,n))
AA=np.zeros((4,4,n))
AA[0:2,2,:]=-1
AA[2:4,3,:]=-1
AA[0::2,0,:]=dxy1[ii,:].T
AA[1::2,1,:]=dxy2[jj,:].T
BB=np.zeros((4,n))
BB[0,:]=-x1[ii].ravel()
BB[1,:]=-x2[jj].ravel()
BB[2,:]=-y1[ii].ravel()
BB[3,:]=-y2[jj].ravel()
for i in range(n):
try:
T[:,i]=np.linalg.solve(AA[:,:,i],BB[:,i])
except:
T[:,i]=np.NaN
in_range= (T[0,:] >=0) & (T[1,:] >=0) & (T[0,:] <=1) & (T[1,:] <=1)
xy0=T[2:,in_range]
xy0=xy0.T
return xy0[:,0],xy0[:,1]
if __name__ == '__main__':
# a piece of a prolate cycloid, and am going to find
a, b = 1, 2
phi = np.linspace(3, 10, 100)
x1 = a*phi - b*np.sin(phi)
y1 = a - b*np.cos(phi)
x2=phi
y2=np.sin(phi)+2
x,y=intersection(x1,y1,x2,y2)
plt.plot(x1,y1,c='r')
plt.plot(x2,y2,c='g')
plt.plot(x,y,'*k')
plt.show()

I didn't find an intuitive explanation on the web, so now that I worked it out, here's my solution. This is for infinite lines (what I needed), not segments.
Some terms you might remember:
A line is defined as y = mx + b OR y = slope * x + y-intercept
Slope = rise over run = dy / dx = height / distance
Y-intercept is where the line crosses the Y axis, where X = 0
Given those definitions, here are some functions:
def slope(P1, P2):
# dy/dx
# (y2 - y1) / (x2 - x1)
return(P2[1] - P1[1]) / (P2[0] - P1[0])
def y_intercept(P1, slope):
# y = mx + b
# b = y - mx
# b = P1[1] - slope * P1[0]
return P1[1] - slope * P1[0]
def line_intersect(m1, b1, m2, b2):
if m1 == m2:
print ("These lines are parallel!!!")
return None
# y = mx + b
# Set both lines equal to find the intersection point in the x direction
# m1 * x + b1 = m2 * x + b2
# m1 * x - m2 * x = b2 - b1
# x * (m1 - m2) = b2 - b1
# x = (b2 - b1) / (m1 - m2)
x = (b2 - b1) / (m1 - m2)
# Now solve for y -- use either line, because they are equal here
# y = mx + b
y = m1 * x + b1
return x,y
Here's a simple test between two (infinite) lines:
A1 = [1,1]
A2 = [3,3]
B1 = [1,3]
B2 = [3,1]
slope_A = slope(A1, A2)
slope_B = slope(B1, B2)
y_int_A = y_intercept(A1, slope_A)
y_int_B = y_intercept(B1, slope_B)
print(line_intersect(slope_A, y_int_A, slope_B, y_int_B))
Output:
(2.0, 2.0)

The most concise solution I have found uses Sympy: https://www.geeksforgeeks.org/python-sympy-line-intersection-method/
# import sympy and Point, Line
from sympy import Point, Line
p1, p2, p3 = Point(0, 0), Point(1, 1), Point(7, 7)
l1 = Line(p1, p2)
# using intersection() method
showIntersection = l1.intersection(p3)
print(showIntersection)

With the scikit-spatial library you can easily do it in the following way:
import matplotlib.pyplot as plt
from skspatial.objects import Line
# Define the two lines.
line_1 = Line.from_points([3, -2], [5, 4])
line_2 = Line.from_points([-1, 0], [3, 2])
# Compute the intersection point
intersection_point = line_1.intersect_line(line_2)
# Plot
_, ax = plt.subplots()
line_1.plot_2d(ax, t_1=-2, t_2=3, c="k")
line_2.plot_2d(ax, t_1=-2, t_2=3, c="k")
intersection_point.plot_2d(ax, c="r", s=100)
grid = ax.grid()

there is already an answer that uses formula from Wikipedia but that doesn't have any check point to check if line segments actually intersect so here you go
def line_intersection(a, b, c, d):
t = ((a[0] - c[0]) * (c[1] - d[1]) - (a[1] - c[1]) * (c[0] - d[0])) / ((a[0] - b[0]) * (c[1] - d[1]) - (a[1] - b[1]) * (c[0] - d[0]))
u = ((a[0] - c[0]) * (a[1] - b[1]) - (a[1] - c[1]) * (a[0] - b[0])) / ((a[0] - b[0]) * (c[1] - d[1]) - (a[1] - b[1]) * (c[0] - d[0]))
# check if line actually intersect
if (0 <= t and t <= 1 and 0 <= u and u <= 1):
return [a[0] + t * (b[0] - a[0]), a[1] + t * (b[1] - a[1])]
else:
return False
#usage
print(line_intersection([0,0], [10, 10], [0, 10], [10,0]))
#result [5.0, 5.0]

img And You can use this kode
class Nokta:
def __init__(self,x,y):
self.x=x
self.y=y
class Dogru:
def __init__(self,a,b):
self.a=a
self.b=b
def Kesisim(self,Dogru_b):
x1= self.a.x
x2=self.b.x
x3=Dogru_b.a.x
x4=Dogru_b.b.x
y1= self.a.y
y2=self.b.y
y3=Dogru_b.a.y
y4=Dogru_b.b.y
#Notlardaki denklemleri kullandım
pay1=((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3))
pay2=((x2-x1) * (y1 - y3) - (y2 - y1) * (x1 - x3))
payda=((y4 - y3) *(x2-x1)-(x4 - x3)*(y2 - y1))
if pay1==0 and pay2==0 and payda==0:
print("DOĞRULAR BİRBİRİNE ÇAKIŞIKTIR")
elif payda==0:
print("DOĞRULAR BİRBİRNE PARALELDİR")
else:
ua=pay1/payda if payda else 0
ub=pay2/payda if payda else 0
#x ve y buldum
x=x1+ua*(x2-x1)
y=y1+ua*(y2-y1)
print("DOĞRULAR {},{} NOKTASINDA KESİŞTİ".format(x,y))

Related

Snap a point to the closest part of a line?

I have a work order management system that has Python 2.7.
In the system, it is only possible to use the libraries that are included in the standard Python 2.7 library (it's not possible to import other libraries).
The system has lines and points:
Line Vertex 1: 676561.00, 4860927.00
Line Vertex 2: 676557.00, 4860939.00
Point 100: 676551.00, 4860935.00
Point 200: 676558.00, 4860922.00
I want to snap the points to the nearest part of the line.
Snapping can be defined as:
Moving a point so that it coincides exactly with the closest part of a line.
There appear to be two different mathematical scenarios at play:
A. The closest part of a line is the position along the line where the point is perpendicular to the line.
B. Or, the closest part of the line is simply the closest vertex.
Is it possible to snap a point to the closest part of a line using Python 2.7 (and the standard 2.7 library)?
This is an extremely helpful resource.
import collections
import math
Line = collections.namedtuple('Line', 'x1 y1 x2 y2')
Point = collections.namedtuple('Point', 'x y')
def lineLength(line):
dist = math.sqrt((line.x2 - line.x1)**2 + (line.y2 - line.y1)**2)
return dist
## See http://paulbourke.net/geometry/pointlineplane/
## for helpful formulas
## Inputs
line = Line(0.0, 0.0, 100.0, 0.0)
point = Point(50.0, 1500)
## Calculations
print('Inputs:')
print('Line defined by: ({}, {}) and ({}, {})'.format(line.x1, line.y1, line.x2, line.y2))
print('Point "P": ({}, {})'.format(point.x, point.y))
len = lineLength(line)
if (len == 0):
raise Exception('The points on input line must not be identical')
print('\nResults:')
print('Length of line (calculated): {}'.format(len))
u = ((point.x - line.x1) * (line.x2 - line.x1) + (point.y - line.y1) * (line.y2 - line.y1)) / (
len**2)
# restrict to line boundary
if u > 1:
u = 1
elif u < 0:
u = 0
nearestPointOnLine = Point(line.x1 + u * (line.x2 - line.x1), line.y1 + u * (line.y2 - line.y1))
shortestLine = Line(nearestPointOnLine.x, nearestPointOnLine.y, point.x, point.y)
print('Nearest point "N" on line: ({}, {})'.format(nearestPointOnLine.x, nearestPointOnLine.y))
print('Length from "P" to "N": {}'.format(lineLength(shortestLine)))
You can calculate the line equation and then the distance between each point and the line.
For instance:
import collections
import math
Point = collections.namedtuple('Point', "x, y")
def distance(pt, a, b, c):
# line eq: ax + by + c = 0
return math.fabs(a * pt.x + b * pt.y + c) / math.sqrt(a**2 + b**2)
l1 = Point(676561.00, 4860927.00)
l2 = Point(676557.00, 4860939.00)
# line equation
a = l2.y - l1.y
b = l1.x - l2.x
c = l2.x * l1.y - l2.y * l1.x
assert a * l1.x + b * l1.y + c == 0
assert a * l2.x + b * l2.y + c == 0
p100 = Point(676551.00, 4860935.00)
p200 = Point(676558.00, 4860922.00)
print(distance(p100, a, b, c))
print(distance(p200, a, b, c))
You get:
6.957010852370434
4.427188724235731
Edit1: calculating the orthographic projection
What you want is the coordinates of the orthographic projection of p100 and p200 on the line (l1, l2).
You can calculate that as follow:
import collections
import math
Point = collections.namedtuple('Point', "x, y")
def snap(pt, pt1, pt2):
# type: (Point, Point, Point) -> Point
v = Point(pt2.x - pt1.x, pt2.y - pt1.y)
dv = math.sqrt(v.x ** 2 + v.y ** 2)
bh = ((pt.x - pt1.x) * v.x + (pt.y - pt1.y) * pt2.y) / dv
h = Point(
pt1.x + bh * v.x / dv,
pt1.y + bh * v.y / dv
)
return h
l1 = Point(676561.00, 4860927.00)
l2 = Point(676557.00, 4860939.00)
p100 = Point(676551.00, 4860935.00)
p200 = Point(676558.00, 4860922.00)
s100 = snap(p100, l1, l2)
s200 = snap(p200, l1, l2)
print(s100)
print(p100)
You get:
Point(x=-295627.7999999998, y=7777493.4)
Point(x=676551.0, y=4860935.0)
You can check that the snapped points are on the line:
# line equation
a = l2.y - l1.y
b = l1.x - l2.x
c = l2.x * l1.y - l2.y * l1.x
assert math.fabs(a * s100.x + b * s100.y + c) < 1e-6
assert math.fabs(a * s200.x + b * s200.y + c) < 1e-6
Edit2: snap to the line segment
If you want to snap to a line segment, you need to check if the orthographic projection is inside the line segment or not.
If the orthographic projection is inside the line segment: it is the solution,
If it is near an extremity of the segment, this extremity is the solution.
You can do that as bellow:
def distance_pts(pt1, pt2):
v = Point(pt2.x - pt1.x, pt2.y - pt1.y)
dv = math.sqrt(v.x ** 2 + v.y ** 2)
return dv
def snap(pt, pt1, pt2):
# type: (Point, Point, Point) -> Point
v = Point(pt2.x - pt1.x, pt2.y - pt1.y)
dv = distance_pts(pt1, pt2)
bh = ((pt.x - pt1.x) * v.x + (pt.y - pt1.y) * pt2.y) / dv
h = Point(pt1.x + bh * v.x / dv, pt1.y + bh * v.y / dv)
if 0 <= (pt1.x - h.x) / (pt2.x - h.y) < 1:
# in the line segment
return h
elif distance_pts(h, pt1) < distance_pts(h, pt2):
# near pt1
return pt1
else:
# near pt2
return pt2
The solutions for p100 and p200 are:
Point(x=676557.0, y=4860939.0)
Point(x=676551.0, y=4860935.0)
Another option:
# snap a point to a 2d line
# parameters:
# A,B: the endpoints of the line
# C: the point we want to snap to the line AB
# all parameters must be a tuple/list of float numbers
def snap_to_line(A,B,C):
Ax,Ay = A
Bx,By = B
Cx,Cy = C
# special case: A,B are the same point: just return A
eps = 0.0000001
if abs(Ax-Bx) < eps and abs(Ay-By) < eps: return [Ax,Ay]
# any point X on the line can be represented by the equation
# X = A + t * (B-A)
# so for point C we compute its perpendicular D on the line
# and the parameter t for D
# if t is between 0..1 then D is on the line so the snap point is D
# if t < 0 then the snap point is A
# if t > 1 then the snap point is B
#
# explanation of the formula for distance from point to line:
# http://paulbourke.net/geometry/pointlineplane/
#
dx = Bx-Ax
dy = By-Ay
d2 = dx*dx + dy*dy
t = ((Cx-Ax)*dx + (Cy-Ay)*dy) / d2
if t <= 0: return A
if t >= 1: return B
return [dx*t + Ax, dy*t + Ay]
if __name__=="__main__":
A,B = [676561.00, 4860927.00],[676557.00, 4860939.00]
C = [676551.00, 4860935.00]
print(snap_to_line(A,B,C))
C = [676558.00, 4860922.00]
print(snap_to_line(A,B,C))

python: interpolation: finding a rectangle that minimally include a point

I am implementing a bilinear interpolation as in How to perform bilinear interpolation in Python
I have a sorted list of points that are the vertexes of my regular grid.
[[x1,y1,z1],[x2,y2,z2],[x3,y3,z3],[x4,y4,z4],[x5,y5,z5],...]
I want to interpolate linearly on the point (x,y). I have written the following code
def f(x, y, points):
for i in range(len(points)-1, -1, -1):
if (x>points[i][0])and(y>points[i][1]):
break
try:
pp = [points[i], points[i+1]]
except IndexError:
pp = [points[i], points[i-1]]
for j in range(len(points)):
if (x<points[j][0])and(y<points[j][1]):
break
pp.append(points[j-1])
pp.append(points[j])
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = pp
return (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1))
but this code doesn't work on the boundaries. I would think this is common problem in interpolation, so I was wondering how I should select the smallest rectangle of points around (x,y) from my regular grid.
Your grid is regular, so you don't need to traverse all points to determine cell indexes. Just divide coordinates by cell size and round result to smaller integer. 1D example: if first point has coordinate 1 and cell size is 2, point 6 lies at int (6-1)/2 = 2-nd interval
Restrict result index to ensure that it is in grid limits - so points outside grid will use border cells
i = int((x - points[i][0]) / xsize) #not sure what is the best way in Python
if (i < 0):
i = 0
if (i >= XCount):
i = XCount - 1
// same for j and y-coordinate
Following the suggestions in the comments I have written the following code:
def f(x, y, points):
points = sorted(points)
xunique = np.unique([point[0] for point in points])
yunique = np.unique([point[1] for point in points])
xmax = np.max(xunique)
ymax = np.max(yunique)
deltax = xunique[1] - xunique[0]
deltay = yunique[1] - yunique[0]
x0 = xunique[0]
y0 = yunique[0]
ni = len(xunique)
nj = len(yunique)
i1 = int(np.floor((x-x0)/deltax))
if i1 == ni:
i1 = i1 - 1
i2 = int(np.ceil((x-x0)/deltax))
if i2 == ni:
i2 = i2 - 1
j1 = int(np.floor((y-y0)/deltay))
if j1 == nj:
j1 = j1 - 1
j2 = int(np.ceil((y-y0)/deltay))
if j2 == ni:
j2 = j2 - 1
pp=[]
if (i1==i2):
if i1>0:
i1=i1-1
else:
i2=i2+1
if (j1==j2):
if j1>0:
j1=j1-1
else:
j2=j2+1
pp=[points[i1 * nj + j1], points[i1 * nj + j2],
points[i2 * nj + j1], points[i2 * nj + j2]]
(x1, y1, q11), (_x1, y2, q12), (x2, _y1, q21), (_x2, _y2, q22) = pp
return (q11 * (x2 - x) * (y2 - y) +
q21 * (x - x1) * (y2 - y) +
q12 * (x2 - x) * (y - y1) +
q22 * (x - x1) * (y - y1)) / ((x2 - x1) * (y2 - y1))

Check if two lines (each with start-end positions) overlap in python

I have the following two sets of position each correspond to the start and end position:
line T: t1-t2 (t1 = start_pos, t2 = end_pos)
line S: s1-s2 (s1 = start_pos, t2 = end_pos)
I want to write the algorithm in Python to check if T intersect with S.
Example 1:
t1-t2=55-122 and s1-s2=58-97
s1------------s2
t1-----------------t2
This should return True
Example 2:
t1-t2=4-66 / t1-t2=143-166 and s1-s2=80-141
s1----s2
t1--t2 t1---t2
Both instances of T should return False
But why this code failed:
def is_overlap(pos, dompos):
"""docstring for is_overlap"""
t1,t2 = [int(x) for x in pos.split("-")]
s1,s2 = [int(x) for x in dompos.split("-")]
# Here we define the instance of overlapness
if (t1 >= s1 and t2 >= s2) or \
(t1 >= s1 and t2 <= s2) or \
(t1 <= s1 and t2 >= s2) or \
(t1 <= s1 and t2 <= s2):
return True
else:
return False
which output this:
In [2]: is_overlap('55-122', '58-97')
Out[2]: True
In [3]: is_overlap('4-66', '80-141')
Out[3]: True
In [4]: is_overlap('143-166', '80-141')
Out[4]: True
What's the right way to do it?
Consider a span [a, b] and another span [x, y]. They either overlap or they are separate.
If they are separate, one of two things must be true:
[a, b] is to the left of [x, y], or
[x, y] is to the left of [a, b].
If [a, b] is to the left of [x, y], we have b < x.
If [x, y] is to the left of [a, b], we have y < a.
If neither of these is true, the spans cannot be separate. They must overlap.
This logic is implemented in the following function.
def are_separate(r, s): # r and s are ordered pairs
(a, b) = r
(x, y) = s
if b < x or y < a:
return True
else:
return False
More concisely:
def are_separate(r, s):
(a, b) = r
(x, y) = s
return b < x or y < a
Even more concisely:
def are_separate(r, s):
return r[1] < s[0] or s[1] < r[0]
If you want the contrary function, are_overlapping, just negate the expression:
def are_overlapping(r, s):
return not(r[1] < s[0] or s[1] < r[0])
Which is logically equivalent to:
def are_overlapping(r, s):
return r[1] >= s[0] and s[1] >= r[0]
Your conditions are wrong; check them again. For instance, the first condition implies that (t_1, t_2) = (100, 200) and (s_1, s_2) = (50, 60) to be a valid set of overlapping lines. But clearly, they aren't.
Something else you might want to consider is if the user inputs the coordinates in backwards. What if he puts in something like '80-30'?
For those who want two lines in two dimensions, #michael's answer is not enough. The following code is based on the equation found in the Wikipedia page listed just above the code as well as a little Pythagoras to check the intersection lies between the appropriate points. https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
x1, y1, x2, y2 = 200, 200, 300, 300
x3, y3, x4, y4 = 200, 150, 300, 350
px = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / \
((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
py = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / \
((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
if ((x1 - px) ** 2 + (y1 - py) ** 2) ** 0.5 + ((x2 - px) ** 2 + (y2 - py) ** 2) ** 0.5 == \
((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5 and \
((x3 - px) ** 2 + (y3 - py) ** 2) ** 0.5 + ((x4 - px) ** 2 + (y4 - py) ** 2) ** 0.5 == \
((x3 - x4) ** 2 + (y3 - y4) ** 2) ** 0.5:
print("Overlap at point: (%s, %s)" % (px, py))
else:
print("No overlap.")

All integer points on a line segment

I'm looking for a short smart way to find all integer points on a line segment. The 2 points are also integers, and the line can be at an angle of 0,45,90,135 etc. degrees.
Here is my long code(so far the 90 degree cases):
def getPoints(p1,p2)
if p1[0] == p2[0]:
if p1[1] < p2[1]:
return [(p1[0],x) for x in range(p1[1],p2[1])]
else:
return [(p1[0],x) for x in range(p1[1],p2[1],-1)]
if p2[1] == p2[1]:
if p1[0] < p2[0]:
return [(x,p1[1]) for x in range(p1[0],p2[0])]
else:
return [(x,p1[1]) for x in range(p1[0],p2[0],-1)]
EDIT: I haven't mentioned it clear enough, but the slope will always be an integer -1, 0 or 1, there are 8 cases that are need to be checked.
Reduce the slope to lowest terms (p/q), then step from one endpoint of the line segment to the other in increments of p vertically and q horizontally. The same code can work for vertical line segments if your reduce-to-lowest-terms code reduces 5/0 to 1/0.
Do a little bit of maths for each pair of points calculate m & c for mx+c and compare it to the formulae for the lines you are considering. (N.B. You Will get some divide by zeros to cope with.)
i could write code that works, but the amount of repeating code is
throwing me off, that's why i turned to you guys
This could stand a lot of improvement but maybe it gets you on the track.
(sorry, don't have time to make it better just now!)
def points(p1,p2):
slope = (p2[1]-p1[1])/float(p2[0]-p1[0])
[(x,x*slope) for x in range (p1[0], p2[0]) if int(x*slope) == x*slope)]
Extending the answer of #Jon Kiparsky.
def points_on_line(p1, p2):
fx, fy = p1
sx, sy = p2
if fx == sx and fy == sy:
return []
elif fx == sx:
return [(fx, y) for y in range(fy+1, sy)]
elif fy == sy:
return [(x, fy) for x in range(fx+1, sx)]
elif fx > sx and fy > sy:
p1, p2 = p2, p1
slope = (p2[1] - p1[1]) / float(p2[0] - p1[0])
return [(x, int(x*slope)) for x in range(p1[0], p2[0]) if int(x*slope) == x*slope and (x, int(x*slope)) != p1]
def getpoints(p1, p2):
# Sort both points first.
(x1, y1), (x2, y2) = sorted([p1, p2])
a = b = 0.0
# Not interesting case.
if x1 == x2:
yield p1
# First point is in (0, y).
if x1 == 0.0:
b = y1
a = (y2 - y1) / x2
elif x2 == 0.0:
# Second point is in (0, y).
b = y2
a = (y1 - y2) / x1
else:
# Both points are valid.
b = (y2 - (y1 * x2) / x1) / (1 - (x2 / x1))
a = (y1 - b) / x1
for x in xrange(int(x1), int(x2) + 1):
y = a * float(x) + b
# Delta could be increased for lower precision.
if abs(y - round(y)) == 0:
yield (x, y)

Numpy and line intersections

How would I use numpy to calculate the intersection between two line segments?
In the code I have segment1 = ((x1,y1),(x2,y2)) and segment2 = ((x1,y1),(x2,y2)). Note segment1 does not equal segment2. So in my code I've also been calculating the slope and y-intercept, it would be nice if that could be avoided but I don't know of a way how.
I've been using Cramer's rule with a function I wrote up in Python but I'd like to find a faster way of doing this.
Stolen directly from https://web.archive.org/web/20111108065352/https://www.cs.mun.ca/~rod/2500/notes/numpy-arrays/numpy-arrays.html
#
# line segment intersection using vectors
# see Computer Graphics by F.S. Hill
#
from numpy import *
def perp( a ) :
b = empty_like(a)
b[0] = -a[1]
b[1] = a[0]
return b
# line segment a given by endpoints a1, a2
# line segment b given by endpoints b1, b2
# return
def seg_intersect(a1,a2, b1,b2) :
da = a2-a1
db = b2-b1
dp = a1-b1
dap = perp(da)
denom = dot( dap, db)
num = dot( dap, dp )
return (num / denom.astype(float))*db + b1
p1 = array( [0.0, 0.0] )
p2 = array( [1.0, 0.0] )
p3 = array( [4.0, -5.0] )
p4 = array( [4.0, 2.0] )
print seg_intersect( p1,p2, p3,p4)
p1 = array( [2.0, 2.0] )
p2 = array( [4.0, 3.0] )
p3 = array( [6.0, 0.0] )
p4 = array( [6.0, 3.0] )
print seg_intersect( p1,p2, p3,p4)
import numpy as np
def get_intersect(a1, a2, b1, b2):
"""
Returns the point of intersection of the lines passing through a2,a1 and b2,b1.
a1: [x, y] a point on the first line
a2: [x, y] another point on the first line
b1: [x, y] a point on the second line
b2: [x, y] another point on the second line
"""
s = np.vstack([a1,a2,b1,b2]) # s for stacked
h = np.hstack((s, np.ones((4, 1)))) # h for homogeneous
l1 = np.cross(h[0], h[1]) # get first line
l2 = np.cross(h[2], h[3]) # get second line
x, y, z = np.cross(l1, l2) # point of intersection
if z == 0: # lines are parallel
return (float('inf'), float('inf'))
return (x/z, y/z)
if __name__ == "__main__":
print get_intersect((0, 1), (0, 2), (1, 10), (1, 9)) # parallel lines
print get_intersect((0, 1), (0, 2), (1, 10), (2, 10)) # vertical and horizontal lines
print get_intersect((0, 1), (1, 2), (0, 10), (1, 9)) # another line for fun
Explanation
Note that the equation of a line is ax+by+c=0. So if a point is on this line, then it is a solution to (a,b,c).(x,y,1)=0 (. is the dot product)
let l1=(a1,b1,c1), l2=(a2,b2,c2) be two lines and p1=(x1,y1,1), p2=(x2,y2,1) be two points.
Finding the line passing through two points:
let t=p1xp2 (the cross product of two points) be a vector representing a line.
We know that p1 is on the line t because t.p1 = (p1xp2).p1=0.
We also know that p2 is on t because t.p2 = (p1xp2).p2=0. So t must be the line passing through p1 and p2.
This means that we can get the vector representation of a line by taking the cross product of two points on that line.
Finding the point of intersection:
Now let r=l1xl2 (the cross product of two lines) be a vector representing a point
We know r lies on l1 because r.l1=(l1xl2).l1=0. We also know r lies on l2 because r.l2=(l1xl2).l2=0. So r must be the point of intersection of the lines l1 and l2.
Interestingly, we can find the point of intersection by taking the cross product of two lines.
This is is a late response, perhaps, but it was the first hit when I Googled 'numpy line intersections'. In my case, I have two lines in a plane, and I wanted to quickly get any intersections between them, and Hamish's solution would be slow -- requiring a nested for loop over all line segments.
Here's how to do it without a for loop (it's quite fast):
from numpy import where, dstack, diff, meshgrid
def find_intersections(A, B):
# min, max and all for arrays
amin = lambda x1, x2: where(x1<x2, x1, x2)
amax = lambda x1, x2: where(x1>x2, x1, x2)
aall = lambda abools: dstack(abools).all(axis=2)
slope = lambda line: (lambda d: d[:,1]/d[:,0])(diff(line, axis=0))
x11, x21 = meshgrid(A[:-1, 0], B[:-1, 0])
x12, x22 = meshgrid(A[1:, 0], B[1:, 0])
y11, y21 = meshgrid(A[:-1, 1], B[:-1, 1])
y12, y22 = meshgrid(A[1:, 1], B[1:, 1])
m1, m2 = meshgrid(slope(A), slope(B))
m1inv, m2inv = 1/m1, 1/m2
yi = (m1*(x21-x11-m2inv*y21) + y11)/(1 - m1*m2inv)
xi = (yi - y21)*m2inv + x21
xconds = (amin(x11, x12) < xi, xi <= amax(x11, x12),
amin(x21, x22) < xi, xi <= amax(x21, x22) )
yconds = (amin(y11, y12) < yi, yi <= amax(y11, y12),
amin(y21, y22) < yi, yi <= amax(y21, y22) )
return xi[aall(xconds)], yi[aall(yconds)]
Then to use it, provide two lines as arguments, where is arg is a 2 column matrix, each row corresponding to an (x, y) point:
# example from matplotlib contour plots
Acs = contour(...)
Bsc = contour(...)
# A and B are the two lines, each is a
# two column matrix
A = Acs.collections[0].get_paths()[0].vertices
B = Bcs.collections[0].get_paths()[0].vertices
# do it
x, y = find_intersections(A, B)
have fun
This is a version of #Hamish Grubijan's answer that also works for multiple points in each of the input arguments, i.e., a1, a2, b1, b2 can be Nx2 row arrays of 2D points. The perp function is replaced by a dot product.
T = np.array([[0, -1], [1, 0]])
def line_intersect(a1, a2, b1, b2):
da = np.atleast_2d(a2 - a1)
db = np.atleast_2d(b2 - b1)
dp = np.atleast_2d(a1 - b1)
dap = np.dot(da, T)
denom = np.sum(dap * db, axis=1)
num = np.sum(dap * dp, axis=1)
return np.atleast_2d(num / denom).T * db + b1
I would like to add something small here. The original question is about line segments. I arrived here, because I was looking for line segment intersection, which in my case meant that I need to filter those cases, where no intersection of the line segments exists. Here is some code which does that:
def line_intersection(x1, y1, x2, y2, x3, y3, x4, y4):
"""find the intersection of line segments A=(x1,y1)/(x2,y2) and
B=(x3,y3)/(x4,y4). Returns a point or None"""
denom = ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
if denom==0: return None
px = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denom
py = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denom
if (px - x1) * (px - x2) < 0 and (py - y1) * (py - y2) < 0 \
and (px - x3) * (px - x4) < 0 and (py - y3) * (py - y4) < 0:
return [px, py]
else:
return None
In case you are looking for a vectorized version where we can rule out vertical line segments.
def intersect(a):
# a numpy array with dimension [n, 2, 2, 2]
# axis 0: line-pair, axis 1: two lines, axis 2: line delimiters axis 3: x and y coords
# for each of the n line pairs a boolean is returned stating of the two lines intersect
# Note: the edge case of a vertical line is not handled.
m = (a[:, :, 1, 1] - a[:, :, 0, 1]) / (a[:, :, 1, 0] - a[:, :, 0, 0])
t = a[:, :, 0, 1] - m[:, :] * a[:, :, 0, 0]
x = (t[:, 0] - t[:, 1]) / (m[:, 1] - m[:, 0])
y = m[:, 0] * x + t[:, 0]
r = a.min(axis=2).max(axis=1), a.max(axis=2).min(axis=1)
return (x >= r[0][:, 0]) & (x <= r[1][:, 0]) & (y >= r[0][:, 1]) & (y <= r[1][:, 1])
A sample invocation would be:
intersect(np.array([
[[[1, 2], [2, 2]],
[[1, 2], [1, 1]]], # I
[[[3, 4], [4, 4]],
[[4, 4], [5, 6]]], # II
[[[2, 0], [3, 1]],
[[3, 0], [4, 1]]], # III
[[[0, 5], [2, 5]],
[[2, 4], [1, 3]]], # IV
]))
# returns [False, True, False, False]
Visualization (I need more reputation to post images here).
Here's a (bit forced) one-liner:
import numpy as np
from scipy.interpolate import interp1d
x = np.array([0, 1])
segment1 = np.array([0, 1])
segment2 = np.array([-1, 2])
x_intersection = interp1d(segment1 - segment2, x)(0)
# if you need it:
y_intersection = interp1d(x, segment1)(x_intersection)
Interpolate the difference (default is linear), and find a 0 of the inverse.
Cheers!
This is what I use to find line intersection, it works having either 2 points of each line, or just a point and its slope. I basically solve the system of linear equations.
def line_intersect(p0, p1, m0=None, m1=None, q0=None, q1=None):
''' intersect 2 lines given 2 points and (either associated slopes or one extra point)
Inputs:
p0 - first point of first line [x,y]
p1 - fist point of second line [x,y]
m0 - slope of first line
m1 - slope of second line
q0 - second point of first line [x,y]
q1 - second point of second line [x,y]
'''
if m0 is None:
if q0 is None:
raise ValueError('either m0 or q0 is needed')
dy = q0[1] - p0[1]
dx = q0[0] - p0[0]
lhs0 = [-dy, dx]
rhs0 = p0[1] * dx - dy * p0[0]
else:
lhs0 = [-m0, 1]
rhs0 = p0[1] - m0 * p0[0]
if m1 is None:
if q1 is None:
raise ValueError('either m1 or q1 is needed')
dy = q1[1] - p1[1]
dx = q1[0] - p1[0]
lhs1 = [-dy, dx]
rhs1 = p1[1] * dx - dy * p1[0]
else:
lhs1 = [-m1, 1]
rhs1 = p1[1] - m1 * p1[0]
a = np.array([lhs0,
lhs1])
b = np.array([rhs0,
rhs1])
try:
px = np.linalg.solve(a, b)
except:
px = np.array([np.nan, np.nan])
return px
We can solve this 2D line intersection problem using determinant.
To solve this, we have to convert our lines to the following form: ax+by=c. where
a = y1 - y2
b = x1 - x2
c = ax1 + by1
If we apply this equation for each line, we will got two line equation. a1x+b1y=c1 and a2x+b2y=c2.
Now when we got the expression for both lines.
First of all we have to check if the lines are parallel or not. To examine this we want to find the determinant. The lines are parallel if the determinant is equal to zero.
We find the determinant by solving the following expression:
det = a1 * b2 - a2 * b1
If the determinant is equal to zero, then the lines are parallel and will never intersect. If the lines are not parallel, they must intersect at some point.
The point of the lines intersects are found using the following formula:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
'''
finding intersect point of line AB and CD
where A is the first point of line AB
and B is the second point of line AB
and C is the first point of line CD
and D is the second point of line CD
'''
def get_intersect(A, B, C, D):
# a1x + b1y = c1
a1 = B.y - A.y
b1 = A.x - B.x
c1 = a1 * (A.x) + b1 * (A.y)
# a2x + b2y = c2
a2 = D.y - C.y
b2 = C.x - D.x
c2 = a2 * (C.x) + b2 * (C.y)
# determinant
det = a1 * b2 - a2 * b1
# parallel line
if det == 0:
return (float('inf'), float('inf'))
# intersect point(x,y)
x = ((b2 * c1) - (b1 * c2)) / det
y = ((a1 * c2) - (a2 * c1)) / det
return (x, y)
I wrote a module for line to compute this and some other simple line operations. It is implemented in c++, so it works very fast. You can install FastLine via pip and then use it in this way:
from FastLine import Line
# define a line by two points
l1 = Line(p1=(0,0), p2=(10,10))
# or define a line by slope and intercept
l2 = Line(m=0.5, b=-1)
# compute intersection
p = l1.intersection(l2)
# returns (-2.0, -2.0)
The reason you would want to use numpy code is because it's faster and it's only really faster when you can broadcast it. The way you make numpy code fast is by doing everything in a series of of numpy operations without loops. If you're not going to do this, don't use numpy.
def line_intersect(x1, y1, x2, y2, x3, y3, x4, y4):
denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
if denom == 0:
return None # Parallel.
ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom
ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom
if 0.0 <= ua <= 1.0 and 0.0 <= ub <= 1.0:
return (x1 + ua * (x2 - x1)), (y1 + ua * (y2 - y1))
return None
However, let's do use numpy:
It's a bit easier to deal with points as complex numbers (x=real, y=imag). That trick is used elsewhere. And rather than a 2d set of elements we use a numpy 1d complex array for the 2d points.
import numpy as np
def find_intersections(a, b):
old_np_seterr = np.seterr(divide="ignore", invalid="ignore")
try:
ax1, bx1 = np.meshgrid(np.real(a[:-1]), np.real(b[:-1]))
ax2, bx2 = np.meshgrid(np.real(a[1:]), np.real(b[1:]))
ay1, by1 = np.meshgrid(np.imag(a[:-1]), np.imag(b[:-1]))
ay2, by2 = np.meshgrid(np.imag(a[1:]), np.imag(b[1:]))
# Note if denom is zero these are parallel lines.
denom = (by2 - by1) * (ax2 - ax1) - (bx2 - bx1) * (ay2 - ay1)
ua = ((bx2 - bx1) * (ay1 - by1) - (by2 - by1) * (ax1 - bx1)) / denom
ub = ((ax2 - ax1) * (ay1 - by1) - (ay2 - ay1) * (ax1 - bx1)) / denom
hit = np.dstack((0.0 <= ua, ua <= 1.0, 0.0 <= ub, ub <= 1.0)).all(axis=2)
ax1 = ax1[hit]
ay1 = ay1[hit]
x_vals = ax1 + ua[hit] * (ax2[hit] - ax1)
y_vals = ay1 + ua[hit] * (ay2[hit] - ay1)
return x_vals + y_vals * 1j
finally:
np.seterr(**old_np_seterr)
Invoking code:
import svgelements as svge
from random import random
import numpy as np
j = svge.Path(svge.Circle(cx=random() * 5, cy=random() * 5, r=random() * 5)).npoint(
np.arange(0, 1, 0.001)
)
k = svge.Path(svge.Circle(cx=random() * 5, cy=random() * 5, r=random() * 5)).npoint(
np.arange(0, 1, 0.001)
)
j = j[:, 0] + j[:, 1] * 1j
k = k[:, 0] + k[:, 1] * 1j
intersects = find_intersections(j, k)
print(intersects)
# Random circles will intersect in 0 or 2 points.
In our code, a and b are segment lists. These expect to be a series of connected points and we mesh them to find any segment n -> n+1 segment that intersects with any or all the other segments.
We return all intersections between the polyline a and the polyline b.
Two tricks (for adaptations):
We mesh all the segments. We check every segment in the polyline a list and every segment in the polyline b list. It's pretty easy to see how you'd arrange this if you wanted other inputs.
Many code examples will check if denom is zero but that's not allowed in pure array code since there's a mesh of different points to check, so conditionals need to be in-lined. We turn off the seterr for dividing by 0 and infinity because we expect to do that if we have parallel lines. Which gets rid of the check for denom being zero. If denom is zero then the lines are parallel which means they either meet at 0 points or infinite many points. The typical conditional checking for the values of ua and ub is done in an array stack of each of the checks which then sees if all of these are true for any elements, and then just returns true for those elements.
If you need the value t or the segments within the lists that intersected this should be readily determined from the ua ub and hit.

Categories