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:
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.
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')