Izak Joubert
Izak Joubert

Reputation: 1001

Speed up shapely buffer

I have different shapely.LineStrings like so: enter image description here

which I then buffer to create a polygon like so: enter image description here

I've played around a bit and found that buffering each line segment is slightly faster than unary_union-ing all the linestrings and then buffering the whole thing together. However I do need the total area of the buffered lines as a shapely polygon as I am using it for intersection detection later. So I end up having to unary_union the buffered polygons to get the overall polygon and this is taking some time (not for this particular example but for other examples with more green lines).

So is there a faster way to get the buffered polygon that I am unaware of?

Here is a reproducible example:

import numpy as np
from shapely.geometry import MultiLineString, LineString, Polygon
from shapely import ops, affinity
import matplotlib.pyplot as plt
from math import atan2, degrees
from descartes.patch import PolygonPatch

if __name__ == '__main__':
    Coords = np.array([
        [0, 0, 0, 0, 'N', 0, 0],
        [0, 1, 0, 'BRANCH', 'N', 0, 0],
        [0, 0, 0, 'BRANCH', 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [-0.85, -0.51, 0, 'BRANCH', 'Y', 45, 0],
        [-0.85, -0.51, 0, 'NODE', 'Y', 45, 0],
        [-1.71, -1.03, 0, 0, 'Y', 45, 0],
        [-1.66, -2.02, 0, 'BRANCH', 'Y', 45, 0],
        [-1.66, -2.02, 0, 'NODE', 'Y', 45, 0],
        [-1.60, -3.02, 0, 'BRANCH', 'Y', 45, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0.90, -0.42, 0, 'BRANCH', 'Y', 45, 0],
        [0.90, -0.42, 0, 'NODE', 'Y', 45, 0],
        [1.81, -0.84, 0, 'BRANCH', 'Y', 45, 0],
        [0, 0, 0, 'BRANCH', 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0],
        [0.10, -0.99, 0, 0, 'Y', 45, 0],
        [-0.69, -1.59, 0, 0, 'Y', 45, 0],
        [-0.53, -2.58, 0, 'BRANCH', 'Y', 45, 0],
        [-0.53, -2.58, 0, 'NODE', 'Y', 45, 0],
    ], dtype=object)

    for ind, coord in enumerate(Coords):
        if coord[3] == 'BRANCH':
            if (coord[0:3] == Coords[ind + 1, 0:3]).all():
                np.delete(Coords, ind, 0)

    lines = []

    j = 0
    for i in range(len(Coords)):
        if (Coords[i, 3] == 'BRANCH') or (i == (len(Coords) - 1)):
            lines.append(Coords[j:i+1].tolist())
            j = i+1

    if not lines:
        Lines = [Coords[:]]
    else:
        Lines = [line for line in lines if len(line) > 1]

    fig, ax = plt.subplots()

    patches = []
    lines = []
    Vs = []
    all_r_lines = []
    texts = []


    for num, line in enumerate(Lines):
        line = np.asarray(line, dtype=object)
        num_coords = line[:, 0:2]
        cumm = 0

        indi_coords = []

        for i, joint in enumerate(line):

            if joint[4] == 'Y' and joint[3] != 'BRANCH':

                """ --------------- BODY -------------------------------- """
                indi_coords.append((joint[0], joint[1]))

                new_coords = ((line[i+1][0]), (line[i+1][1]))
                angle = degrees(atan2(
                    (new_coords[1] - joint[1]),
                    (new_coords[0] - joint[0])
                ))

                if cumm > 0:
                    Lines[num][i][6] = cumm

                cumm += 1

            else:
                indi_coords.append((joint[0], joint[1]))
                cumm = 0

        lines.append(np.asarray(indi_coords))

    linestring = MultiLineString(lines)

    for num, line_coords in reversed(list(enumerate(Lines))):
        for i, joint in reversed(list(enumerate(line_coords))):

            if joint[4] == 'Y' and i < (len(Coords)-1) and joint[3] != 'BRANCH':

                if joint[6] > 0:
                    """ --------------- PATCH -------------------------------- """
                    lineA = LineString([(joint[0], joint[1]),
                                        ((line_coords[i+1][0]), (line_coords[i+1][1]))])
                    left_line = affinity.rotate(
                        lineA, joint[5]/2, (joint[0], joint[1]))
                    rigt_line = affinity.rotate(
                        lineA, -joint[5]/2, (joint[0], joint[1]))

                    try:
                        Vs[-1] = ops.unary_union([MultiLineString(
                            [lineA, left_line, rigt_line])] + all_r_lines[-1])
                    except:
                        Vs.append(MultiLineString([lineA, left_line, rigt_line]))

                    """ --------------- ANGLE LINES -------------------------------- """

                    rotate_angle = line_coords[i-1][5]/2
                    r_lines = [affinity.rotate(
                        Vs[-1],
                        j,
                        (line_coords[i-1][0], line_coords[i-1][1])
                    ) for j in np.linspace(-rotate_angle, rotate_angle, num=3)
                    ]

                    all_r_lines += [r_lines]

                    Vs[-1] = ops.unary_union([Vs[-1]] + r_lines)

                else:
                    """ --------------- PATCH -------------------------------- """
                    lineA = LineString([(joint[0], joint[1]),
                                        ((line_coords[i+1][0]), (line_coords[i+1][1]))])
                    left_line = affinity.rotate(
                        lineA, joint[5]/2, (joint[0], joint[1]))
                    rigt_line = affinity.rotate(
                        lineA, -joint[5]/2, (joint[0], joint[1]))

                    Vs.append(MultiLineString([lineA, left_line, rigt_line]))

                    all_r_lines = []

    all_lines = Vs

    a = ops.unary_union(all_lines)

    creature = (Vs + [a] + [linestring])

    polies = []
    for l in creature:
        polies.append(Polygon(l.buffer(0.5)))

    creature_poly = ops.unary_union(polies)
    creature_patch = PolygonPatch(creature_poly, fc='BLUE', alpha=0.1)

    absorbA = creature_poly
    moves = Vs

    for c_l in linestring:
        x, y = c_l.xy
        ax.plot(x, y)

    for m in all_lines:
        for line in m:
            x, y = line.xy
            ax.plot(x, y, 'g--', alpha=0.25)

    ax.axis('equal')

    ax.add_patch(creature_patch)

    ax.axis('equal')
    plt.show()

Upvotes: 2

Views: 6238

Answers (1)

trideset3
trideset3

Reputation: 85

Have you tried shapelys cascaded_union?

    polygons = [Point(i, 0).buffer(0.7) for i in range(5)]

    cascaded_union(polygons)

or in your case Line instead of Point?

https://shapely.readthedocs.io/en/stable/manual.html#shapely.ops.cascaded_union

Upvotes: 1

Related Questions