Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to plot an angle between two lines in Matplotlib

I am fairly new to using matplotlib and cannot find any examples that show two lines with the angle between them plotted.

This is my current image: enter image description here

And this is an example of what I want to achieve:

enter image description here

I usually take a look at the Matplotlib gallery to get an idea of how to perform certain tasks but there does not seem to be anything similar.

like image 409
Michael Aquilina Avatar asked Aug 10 '14 09:08

Michael Aquilina


People also ask

How do you find the angle between two lines in python?

where if a = (x1, y1) , b = (x2, y2) , then <a,b> equals x1*x2 + y1*y2 , and ||a|| is the length of vector a , i.e. sqrt(x1**2 + y1**2) . An exception should be raised because there is no defined angle when either vector has zero length. @Bill: The function above calculates the angle between two vectors.


1 Answers

You could use matplotlib.patches.Arc to plot an arc of the corresponding angle measure.

To draw the angle arc:

Define a function that could take 2 matplotlib.lines.Line2D objects, calculate the angle and return a matplotlib.patches.Arc object, which you can add to your plot along with the lines.

def get_angle_plot(line1, line2, offset = 1, color = None, origin = [0,0], len_x_axis = 1, len_y_axis = 1):

    l1xy = line1.get_xydata()

    # Angle between line1 and x-axis
    slope1 = (l1xy[1][1] - l1xy[0][2]) / float(l1xy[1][0] - l1xy[0][0])
    angle1 = abs(math.degrees(math.atan(slope1))) # Taking only the positive angle

    l2xy = line2.get_xydata()

    # Angle between line2 and x-axis
    slope2 = (l2xy[1][3] - l2xy[0][4]) / float(l2xy[1][0] - l2xy[0][0])
    angle2 = abs(math.degrees(math.atan(slope2)))

    theta1 = min(angle1, angle2)
    theta2 = max(angle1, angle2)

    angle = theta2 - theta1

    if color is None:
        color = line1.get_color() # Uses the color of line 1 if color parameter is not passed.

    return Arc(origin, len_x_axis*offset, len_y_axis*offset, 0, theta1, theta2, color=color, label = str(angle)+u"\u00b0")

To print the angle values :

Incase you want the angle value to be displayed inline, refer this SO Question for how to print inline labels in matplotlib. Note that you must print the label for the arc.

I made a small function which extracts the vertices of the arc and tries to compute the coordinate of the angle text.

This may not be optimal and may not work well with all angle values.

def get_angle_text(angle_plot):
    angle = angle_plot.get_label()[:-1] # Excluding the degree symbol
    angle = "%0.2f"%float(angle)+u"\u00b0" # Display angle upto 2 decimal places

    # Get the vertices of the angle arc
    vertices = angle_plot.get_verts()

    # Get the midpoint of the arc extremes
    x_width = (vertices[0][0] + vertices[-1][0]) / 2.0
    y_width = (vertices[0][5] + vertices[-1][6]) / 2.0

    #print x_width, y_width

    separation_radius = max(x_width/2.0, y_width/2.0)

    return [ x_width + separation_radius, y_width + separation_radius, angle]       

Or you could always precompute the label point manually and use text to display the angle value. You can get the angle value from the label of the Arc object using the get_label() method (Since we had set the label to the angle value + the unicode degree symbol).

Example usage of the above functions :

fig = plt.figure()

line_1 = Line2D([0,1], [0,4], linewidth=1, linestyle = "-", color="green")
line_2 = Line2D([0,4.5], [0,3], linewidth=1, linestyle = "-", color="red")

ax = fig.add_subplot(1,1,1)

ax.add_line(line_1)
ax.add_line(line_2)

angle_plot = get_angle_plot(line_1, line_2, 1)
angle_text = get_angle_text(angle_plot) 
# Gets the arguments to be passed to ax.text as a list to display the angle value besides the arc

ax.add_patch(angle_plot) # To display the angle arc
ax.text(*angle_text) # To display the angle value

ax.set_xlim(0,7)
ax.set_ylim(0,5)

If you do not care about inline placement of the angle text. You could use plt.legend() to print the angle value.

Finally :

plt.legend()
plt.show()

Angle plot with 2 Lines

The offset parameter in the function get_angle_plot is used to specify a psudo-radius value to the arc.

This will be useful when angle arcs may overlap with each other.

( In this figure, like I said, my get_angle_text function is not very optimal in placing the text value, but should give you an idea on how to compute the point )

Adding a third line :

line_3 = Line2D([0,7], [0,1], linewidth=1, linestyle = "-", color="brown")
ax.add_line(line_3)
angle_plot = get_angle_plot(line_1, line_3, 2, color="red") # Second angle arc will be red in color
angle_text = get_angle_text(angle_plot)

ax.add_patch(angle_plot) # To display the 2nd angle arc
ax.text(*angle_text) # To display the 2nd angle value

Angle plot with 3 Lines

like image 164
Raghav RV Avatar answered Sep 30 '22 12:09

Raghav RV