I wonder if you would be interested to add the LANG algorythm to your simplification library.
I already used you library for simplification with ramer-douglas-peucker and visvalingam-whyatt, but when testing some different algorythms to simplify vectors that resulted from a vectorizing raster images I had the best results with LANG.
So, I implemented LANG (or at least something very similar to it) in python.
However, performance wise, it should (at least might) be significantly faster in rust... and since I am facing a bottleneck caused by the simplification in some processes... I wondered...
This is the code in python:
def simplify_coords_lang(
coords: Union[np.ndarray, list],
tolerance: float,
lookahead: int) -> Union[np.ndarray, list]:
"""
Simplify a line using lang algorithm.
Args:
coords (Union[np.ndarray, list]): list of coordinates to be simplified.
tolerance (float): distance tolerance to use.
lookahead (int, optional): the number of points to consider for removing
in a moving window. Defaults to 8.
Returns:
Return the coordinates kept after simplification.
If input coords is np.ndarray, returns np.ndarray, otherwise returns a list.
"""
# Init variables
if isinstance(coords, np.ndarray):
coords_arr = coords
else:
coords_arr = np.array(list(coords))
# Determine the coordinates that need to be kept
coords_to_keep_idx = simplify_coords_lang_idx(
coords=coords_arr,
tolerance=tolerance,
lookahead=lookahead)
coords_simplified_arr = coords_arr[coords_to_keep_idx]
# If input was np.ndarray, return np.ndarray, otherwise list
if isinstance(coords, np.ndarray):
return coords_simplified_arr
else:
return coords_simplified_arr.tolist()
def simplify_coords_lang_idx(
coords: Union[np.ndarray, list],
tolerance: float,
lookahead: int = 8) -> Union[np.ndarray, list]:
"""
Simplify a line using lang algorithm and return the coordinate indexes to
be kept.
Inspiration for the implementation came from:
* https://github.com/giscan/Generalizer/blob/master/simplify.py
* https://github.com/keszegrobert/polyline-simplification/blob/master/6.%20Lang.ipynb
* https://web.archive.org/web/20171005193700/http://web.cs.sunyit.edu/~poissad/projects/Curve/about_algorithms/lang.php
Args:
coords (Union[np.ndarray, list]): list of coordinates to be simplified.
tolerance (float): distance tolerance to use.
lookahead (int, optional): the number of points to consider for removing
in a moving window. Defaults to 8.
Returns:
Return the indexes of coordinates that need to be kept after
simplification.
If input coords is np.ndarray, returns np.ndarray, otherwise returns a list.
"""
def point_line_distance(point_x, point_y, line_x1, line_y1, line_x2, line_y2) -> float:
denominator = math.sqrt((line_x2-line_x1)*(line_x2-line_x1)+(line_y2-line_y1)*(line_y2-line_y1))
if denominator == 0:
return float('Inf')
else:
numerator = abs((line_x2-line_x1)*(line_y1-point_y)-(line_x1-point_x)*(line_y2-line_y1))
return numerator/denominator
# Init variables
if isinstance(coords, np.ndarray):
line_arr = coords
else:
line_arr = np.array(list(coords))
# Prepare lookahead
nb_points = len(line_arr)
if lookahead == -1:
window_size = nb_points-1
else:
window_size = min(lookahead, nb_points-1)
#mask = np.ones(nb_points), dtype='bool')
mask = np.zeros(nb_points, dtype='bool')
mask[0] = True
window_start = 0
window_end = window_size
# Apply simplification till the window_start arrives at the last point.
ready = False
while ready is False:
# Check if all points between window_start and window_end are within
# tolerance distance to the line (window_start, window_end).
all_points_in_tolerance = True
for i in range(window_start+1, window_end):
distance = point_line_distance(
line_arr[i, 0], line_arr[i, 1],
line_arr[window_start, 0], line_arr[window_start, 1],
line_arr[window_end, 0], line_arr[window_end, 1])
# If distance is nan (= linepoint1 == linepoint2) or > tolerance
if distance > tolerance:
all_points_in_tolerance = False
break
# If not all the points are within the tolerance distance...
if not all_points_in_tolerance:
# Move window_end to previous point, and try again
window_end -= 1
else:
# All points are within the tolerance, so they can be masked
mask[window_end] = True
#mask[window_start+1:window_end-1] = False
# Move the window forward
window_start = window_end
if window_start == nb_points - 1:
ready = True
window_end = window_start + window_size
if window_end >= nb_points:
window_end = nb_points - 1
# Prepare result: convert the mask to a list of indices of points to keep.
idx_to_keep_arr = mask.nonzero()[0]
# If input was np.ndarray, return np.ndarray, otherwise list
if isinstance(coords, np.ndarray):
return idx_to_keep_arr
else:
return idx_to_keep_arr.tolist()
Hello!
I wonder if you would be interested to add the LANG algorythm to your simplification library.
I already used you library for simplification with ramer-douglas-peucker and visvalingam-whyatt, but when testing some different algorythms to simplify vectors that resulted from a vectorizing raster images I had the best results with LANG.
So, I implemented LANG (or at least something very similar to it) in python.
However, performance wise, it should (at least might) be significantly faster in rust... and since I am facing a bottleneck caused by the simplification in some processes... I wondered...
This is the code in python: