Description
Given a polyline defined by an ordered list of 2D coordinates, return a list of n equidistant coordinates on that polyline.
Sample input:
coords = [
(-4, 1), (-1, 1), ( 0, 4),
( 1, 1), ( 4, 1), ( 2, -1),
( 3, -4), ( 0, -2), (-3, -4),
(-2, -1), (-4, 1)
]
Solution
import math
def point_dist(p1, p2):
"""Distance between two points"""
return math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)
def p3_along_p1_p2(p1, p2, dist):
"""Returns (x, y) point at a given distance from p1 and along the
line created by p1 and p2
"""
dist_btw = point_dist(p1, p2)
dist_ratio = dist / dist_btw
x = (1 - dist_ratio) * p1[0] + dist_ratio * p2[0]
y = (1 - dist_ratio) * p1[1] + dist_ratio * p2[1]
return x, y
def point_seq_length(coords):
"""Compute distance of sequential (x, y) list along a route"""
return sum([point_dist(p1, p2) for p1, p2 in zip(coords, coords[1:])])
def equidist_pts_on_line(coords, n_pts):
"""Equidistant points along route
coords: sequential (x, y) list along a route
returns: (eq_coords, segment_len)
"""
route_len = point_seq_length(coords)
segment_len = route_len / (n_pts - 1)
segment_len_remain = segment_len
curr_p = coords[0]
eq_coords = [coords[0], ] # add the same first coordinate
next_coord_idx = 1
while True:
next_p = coords[next_coord_idx]
dist_to_next = point_dist(curr_p, next_p)
if segment_len_remain > dist_to_next:
segment_len_remain -= dist_to_next
curr_p = next_p
next_coord_idx += 1
else: # segment_len_remain <= dist_to_next
# place an eq_coord segment_len_remain distance from curr_p
# on the line created by curr_p and next_p
eq_coord = p3_along_p1_p2(curr_p, next_p, segment_len_remain)
eq_coords.append(eq_coord)
curr_p = eq_coord # update curr_p to our new point
if len(eq_coords) == n_pts: # solve condition
break
# do not update next_coord_idx
# recompute these to reduce accumulated error
route_len = point_seq_length([curr_p] + coords[next_coord_idx:])
segment_len = route_len / (n_pts - len(eq_coords))
segment_len_remain = segment_len
return eq_coords
Plot our solution with:
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
coords = [
(-4, 1), (-1, 1), ( 0, 4),
( 1, 1), ( 4, 1), ( 2, -1),
( 3, -4), ( 0, -2), (-3, -4),
(-2, -1), (-4, 1)
]
eq_coords = equidist_pts_on_line(coords, 20)
fig, ax = plt.subplots(figsize=(5, 5))
ax.plot([p[0] for p in coords], [p[1] for p in coords], 'o-',
label='input')
ax.plot([p[0] for p in eq_coords], [p[1] for p in eq_coords], 'o',
color='red', label='output')
ax.legend()
ax.set_aspect('equal')
fig.savefig('Basic.png', dpi=150, facecolor='white');