pythonmatplotlib

'offset points' in matplotlib.pyplot.annotate gives unexpected results


I am using the following code to generate a plot with a sine curve marked with 24 'hours' over 360 degrees. Each 'hour' is annotated, however the arrow lengths decrease (shrivel?) with use and even their direction is incorrect.

The X axis spans 360 degrees whereas the Y axis spans 70 degrees. The print statement verifies that the arrows on 6 and 18 hours have the same length and are vertical, according to the offsets specified. This is not so as seen in the resulting plot: enter image description here

Matplotlib version = 3.9.2; Numpy version = 2.1.1

Here is the python code:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

arrow_style = {'arrowstyle': '-|>'}

def make_plot():

    fig, ax = plt.subplots()
    ax.axis([0,+360,-35,+35])
    ax.set(xlabel = 'X (degrees)', ylabel = 'Y (degrees)', title='Vanishing arrow length example')

    degree = np.pi / 180.0  # degrees to radians
    arrow_angle = 72.0      # degrees
    arrow_length = 27.0
    eps = 23.43927945

    eps_x = np.linspace(0, 360, 200)
    eps_y = -eps * np.sin(2 * np.pi * eps_x / 360)
    ax.plot(eps_x, eps_y, 'r')

    hr_x = np.linspace(0, 360, 25)
    hr_y = -eps * np.sin(2 * np.pi * hr_x / 360)

    i = 0
    for x in hr_x:
        ax.plot(hr_x[i], hr_y[i],'bo')
        if hr_y[i] > 0 and arrow_angle > 0: arrow_angle = -75   # degrees
        arrow_x = np.cos(arrow_angle*degree) * arrow_length
        arrow_y = np.sin(arrow_angle*degree) * arrow_length
        ax.annotate(str(i), xy=(hr_x[i], hr_y[i]), xytext=(arrow_x, arrow_y), \
                    xycoords='data', textcoords='offset points', arrowprops=arrow_style)
        print("arrow_x {:.2f}  arrow_y {:.2f}  arrow_angle {:.1f} at {} hours" \
              .format(arrow_x, arrow_y, arrow_angle, i))

        if hr_y[i] <= 0: arrow_angle += 3.0
        if hr_y[i] > 0:  arrow_angle -= 3.0
        i += 1

    ax.grid()
    ax.axhline(0.0)
    ax.axvline(180.0)
    plt.show()

    return fig

make_plot().savefig('vanishing_arrow_length.png')

I have only a few days experience with matplotlib so I guess this is a really simple user error. However the documentation is no help to me in this case.


Solution

  • By default the text is aligned at its bottom left corner. You might want to change this to align at its centre.

    ax.annotate(str(i), xy=(hr_x[i], hr_y[i]), xytext=(arrow_x, arrow_y),
                xycoords='data', textcoords='offset points', arrowprops=arrow_style,
                verticalalignment='center', horizontalalignment='center')
    
    

    enter image description here