def linestring_intersection(target: LineString,
                            tool: LineString) -> List[float]:
    """will return a list of parameters for the target where the tool intersects the target. Parameters are a floating point number where
	0.0 <= parameter <= len(linestring)-1.0
	Parameters that are rounded to the nearest integer are the same as the index of target. Intermediate values represent the interpolated point between vertices on the target linestring."""
    intersection_parameters = []
    for i, (a, b) in enumerate(pairwise(target)):
        for j, (c, d) in enumerate(pairwise(tool)):
            # print(f"{i},{i + 1} against {j + i + 2},{j + i + 1 + 2}")
            intersection_result = linesegment_intersection(a, b, c, d)
            if intersection_result is not None:
                # TODO: collect p to prevent recalculation?
                p, t1, t2 = intersection_result
                if 0 <= t1 <= 1 and 0 <= t2 <= 1:
                    param_1 = i + t1

                    print(param_1)
                    intersection_parameters.append(param_1)

    last_item = float("inf")
    output = []
    for item in sorted(intersection_parameters):
        if not math.isclose(last_item, item):
            output.append(item)
            last_item = item

    return output
def linestring_remove_circle(circle_center: Vector2, radius: float,
                             line_string: LineString) -> List[LineString]:
    results: List[LineString] = []
    sub_result: LineString = []

    for line_segment in pairwise(line_string):
        relation, segments = remove_circle_from_linesegment(
            circle_center, radius, line_segment)

        if relation == interval_tools.SUB_RESULT_ALL:
            if len(sub_result) == 0:
                sub_result.append(line_segment[0])
            sub_result.append(line_segment[1])
        else:
            if relation & interval_tools.SUB_RESULT_START:
                if len(sub_result) == 0:
                    sub_result.append(line_segment[0])
                sub_result.append(segments[0][1])
                results.append(sub_result)
                sub_result = []

            if relation & interval_tools.SUB_RESULT_END:
                sub_result.append(segments[-1][0])
                sub_result.append(line_segment[1])

    if sub_result:
        results.append(sub_result)
    return results
def project_point_onto_linestring_distance_along(
        target: LineString, tool: Vector2) -> Tuple[float, Vector2, float]:
    """returns the distance to the linestring (squared), and the point on the line, and the distance along the line"""
    min_mag_squared = float('inf')
    dist_along_at_min_mag_squared = None
    point = None
    dist_along_so_far = 0
    for segment in pairwise(target):
        c = tool
        a, b = segment
        # project c onto ab,
        ab = b - a
        ac = c - a
        ab_magnitude = ab.magnitude
        # p is the projection of c onto ab, clamped between a and b
        p_scalar = clamp_zero_to_one(ac.dot(ab) / ab.magnitude_squared)
        p = a + ab.scaled(p_scalar)
        pc_magnitude_squared = (p - c).magnitude_squared
        if min_mag_squared > pc_magnitude_squared:
            point = p
            min_mag_squared = pc_magnitude_squared
            dist_along_at_min_mag_squared = dist_along_so_far + p_scalar * ab_magnitude
        dist_along_so_far += ab_magnitude

    return min_mag_squared, point, dist_along_at_min_mag_squared
Example #4
0
def linestring_cut(
    linestring: LineString, normalised_distance_along: float
) -> Tuple[Optional[LineString], Optional[LineString]]:
    """cut linestring at normalised distance along and returns a pair of linestrings"""
    measured_linestring, total_length = linestring_measure(linestring)
    distance_along = total_length * normalised_distance_along

    if distance_along < 0.0 or math.isclose(distance_along, 0.0):
        return None, linestring
    if distance_along > total_length or math.isclose(distance_along,
                                                     total_length):
        return linestring, None
    else:
        distance_remaining = distance_along
        for index, ((a, segment_length),
                    (b, _)) in enumerate(pairwise(measured_linestring)):
            if math.isclose(0, distance_remaining):
                return (linestring[:index + 1], linestring[index:])
            elif distance_remaining < segment_length:
                ab = b - a
                intermediate_point = a + ab / segment_length * distance_remaining

                return (linestring[:index + 1] + [intermediate_point],
                        [intermediate_point] + linestring[index + 1:])
            distance_remaining -= segment_length
Example #5
0
def test_remove_circles_from_linestring():
	a = [Vector2(a, b) for a, b in [[-8.86, 0.99], [-4.98, 4.23], [2.86, 2.95], [4.66, 4.03], [3.34, 6.39], [1.98, 7.39], [-2.34, 7.79]]]
	c = [Vector2(a, b) for a, b in [[-1.98, 4.51], [-0.9, 9.55], [0.98, 9.43], [6.18, 4.19], [2.22, 1.31], [4.74, 7.39], [-6.678259236067627, 2.8118659987476518]]]
	r = 2
	res = remove_circles_from_linestring_2(linestring_measure(a), c, r)
	result = []
	for ls in res:
		for a,b in pairwise(ls):
			result.append(f"Segment(({a.x},{a.y}),({b.x},{b.y}))")
	print(result)
def linestring_interpolate(linestring: List[Vector2], real_distance_along: float):
	dist = 0
	for a, b in pairwise(linestring):
		ab = b - a
		ab_len = ab.magnitude
		if dist + ab_len >= real_distance_along:
			return a + ab.scaled((real_distance_along - dist) / ab_len)
		dist += ab_len
	
	return a + ab.scaled((real_distance_along - dist + 1) / ab_len)
def linestring_interpolate_normalised(linestring: List[Vector2], normalised_distance: float):
	linestring_measured, total_length = linestring_measure(linestring)
	real_distance_along = normalised_distance * total_length
	
	dist = 0
	for (a, ab_len), (b, _) in pairwise(linestring_measured):
		ab = b - a
		if dist + ab_len >= real_distance_along:
			return a + ab.scaled((real_distance_along - dist) / ab_len)
		dist += ab_len
	
	return a + ab.scaled((real_distance_along - dist + 1) / ab_len)
Example #8
0
def linestring_direction(line_string: LineString,
                         normalised_distance_along: float) -> Vector2:
    """returns the direction (as a unit vector) of a linestring segment which contains the point"""
    measured_linestring, line_string_length = linestring_measure(line_string)
    de_normalised_distance_along = line_string_length * normalised_distance_along
    len_so_far = 0
    for (a, ab_len), (b, _) in pairwise(measured_linestring):
        ab = b - a
        len_so_far += ab_len
        if len_so_far >= de_normalised_distance_along:
            return ab / ab_len
    return ab / ab_len
def linestring_measure(
        linestring: List[Vector2]
) -> Tuple[List[Tuple[Vector2, float]], float]:
    """:returns: ([(point:vector, dist_to_next_point:float), ...], total_length:float)"""
    result = []
    total_length = 0
    for a, b in pairwise(linestring):
        ab_len = (b - a).magnitude
        result.append((a, ab_len))
        total_length += ab_len
    result.append((b, 0))
    return result, total_length
def remove_circles_from_linestring_2(measured_linestring: MeasuredLineString,
                                     circle_centers: List[Vector2],
                                     radius: float):
    """Second version of this operation that is substantially optimised"""
    result_linestrings = []
    current_linestring = []

    for (a, ab_length), (b, _) in pairwise(measured_linestring[0]):
        (first_parameter,
         last_parameter), line_segments = remove_circles_from_linesegment(
             (a, b), ab_length, circle_centers, radius)

        if not line_segments:
            # nothing to add
            continue
        elif len(line_segments) == 1:
            if first_parameter != 0:
                if current_linestring:
                    result_linestrings.append(current_linestring)
                    current_linestring = []
                current_linestring.append(line_segments[0][0])
            current_linestring.append(line_segments[0][1])
            if last_parameter != 1:
                result_linestrings.append(current_linestring)
                current_linestring = []
        else:
            first_segment = line_segments[0]
            middle_segments = line_segments[1:-1]
            last_segment = line_segments[-1]

            if first_parameter == 0:
                if current_linestring:
                    current_linestring.append(first_segment[1])
                else:
                    current_linestring.extend(first_segment)
                result_linestrings.append(current_linestring)
                current_linestring = []
            else:
                if current_linestring:
                    result_linestrings.append(current_linestring)
                    current_linestring = []
                result_linestrings.append(list(first_segment))

            result_linestrings.extend(list(item) for item in middle_segments)

            if last_parameter == 1:
                current_linestring.extend(last_segment)
            else:
                result_linestrings.append(list(last_segment))

    return result_linestrings
def linestring_intersection_with_self(inp: LineString) -> List[float]:
    intersection_parameters = []
    for i, (a, b) in enumerate(pairwise(inp)):
        for j, (c, d) in enumerate(pairwise(inp[i + 2:])):
            # print(f"{i},{i + 1} against {j + i + 2},{j + i + 1 + 2}")
            intersection_result = linesegment_intersection(a, b, c, d)
            if intersection_result is not None:
                # TODO: collect p to prevent recalculation?
                p, t1, t2 = intersection_result
                if 0 <= t1 <= 1 and 0 <= t2 <= 1:
                    param_1 = i + t1
                    param_2 = j + i + 2 + t2
                    # print((param_1, param_2))
                    intersection_parameters.append(param_1)
                    intersection_parameters.append(param_2)
    last_item = float("inf")
    output = []
    for item in sorted(intersection_parameters):
        if not math.isclose(last_item, item):
            output.append(item)
            last_item = item

    return output
def project_point_onto_linestring(target: LineString,
                                  tool: Vector2) -> Tuple[float, Vector2]:
    """returns the distance to the linestring, and the point on the line"""
    min_mag_squared = float('inf')
    point = None

    for segment in pairwise(target):
        c = tool
        a, b = segment
        # project c onto ab,
        ab = b - a
        ac = c - a
        # p is the projection of c onto ab
        p_scalar = clamp_zero_to_one(ac.dot(ab) / ab.magnitude_squared)
        p = a + ab.scaled(p_scalar)
        pc_magnitude_squared = (p - c).magnitude_squared
        if min_mag_squared > pc_magnitude_squared:
            point = p
            min_mag_squared = pc_magnitude_squared
    return min_mag_squared, point
def linestring_length(linestring: List[Vector2]):
    result = 0
    for a, b in pairwise(linestring):
        result += (b - a).magnitude
    return result