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)