Matplotlib - rotating text on log scale where angles are incorrectly rounded
I am trying to have text rotate onto a plot which is shown on log scale. When I compute the angles (based on the solution in this answer) the angles are getting incorrectly rounded to 0 or 90 degrees. This is because the angles are computed on a linear scale first, and then transformed. This calculation in linear space is the cause of the trouble. Even in a situation where I know the gradient, (either in a linear or logarithmic scale), I am not sure how I can put this onto the graph correctly.
MWE
import matplotlib as mpl
rc_fonts = {
"text.usetex": True,
'text.latex.preview': True,
"font.size": 50,
'mathtext.default': 'regular',
'axes.titlesize': 55,
"axes.labelsize": 55,
"legend.fontsize": 50,
"xtick.labelsize": 50,
"ytick.labelsize": 50,
'figure.titlesize': 55,
'figure.figsize': (10, 6.5), # 15, 9.3
'text.latex.preamble': [
r"""usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}
"""],
"font.family": "serif",
"font.serif": "computer modern roman",
}
mpl.rcParams.update(rc_fonts)
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, InsetPosition, mark_inset
import numpy as np
x = np.linspace(0, 20, 100)
y = np.exp(x**2)
g = 2*x*y # Gradient.
lg = 2 * x # Gradient on a log scale.
plt.clf()
plt.plot(x, y)
plt.yscale('log')
for x in [0,2,4,7,18]:
angle_data = np.rad2deg(np.arctan2(2 * x * np.exp(x**2), 1))
y = np.exp(x**2)
angle_screen = plt.gca().transData.transform_angles(np.array((angle_data,)), np.array([x, y]).reshape((1, 2)))[0]
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=angle_screen, horizontalalignment='center')
plt.ylim(1e0, 1e180)
plt.xlim(-1, 20)
plt.xlabel(r'$x$')
plt.title(r'$exp(x^2)$', y=1.05)
plt.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
A few ideas?
I had tried to use the fact that for very large functions I can calculate the difference from 90 degrees using arctan(x) ~ pi/2 - arctan(1/x), and the former angle uses the low angle approximation so is just 1/x. However, after plugging this into transform_angles
this is rounded incorrectly.
A slight hack of a solution
If I guess the aspect ratio of the figure (c0.6) and then also adjust for the difference in scales (x in [0:20]
while log10(y) is in [0:180]
, giving a difference of 9 in scale), then I can get the following, although I don't think this is particularly sustainable, especially if I want to tweak something later.
# The 9 comes from tha fact that x is in [0:20], log10(y) is in [0, 180]. The factor of 0.6 is roughly the aspect ratio of the main plot shape.
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=np.rad2deg(np.arctan(0.6 * x/9.0)), horizontalalignment='center')
python matplotlib rotation logarithm
add a comment |
I am trying to have text rotate onto a plot which is shown on log scale. When I compute the angles (based on the solution in this answer) the angles are getting incorrectly rounded to 0 or 90 degrees. This is because the angles are computed on a linear scale first, and then transformed. This calculation in linear space is the cause of the trouble. Even in a situation where I know the gradient, (either in a linear or logarithmic scale), I am not sure how I can put this onto the graph correctly.
MWE
import matplotlib as mpl
rc_fonts = {
"text.usetex": True,
'text.latex.preview': True,
"font.size": 50,
'mathtext.default': 'regular',
'axes.titlesize': 55,
"axes.labelsize": 55,
"legend.fontsize": 50,
"xtick.labelsize": 50,
"ytick.labelsize": 50,
'figure.titlesize': 55,
'figure.figsize': (10, 6.5), # 15, 9.3
'text.latex.preamble': [
r"""usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}
"""],
"font.family": "serif",
"font.serif": "computer modern roman",
}
mpl.rcParams.update(rc_fonts)
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, InsetPosition, mark_inset
import numpy as np
x = np.linspace(0, 20, 100)
y = np.exp(x**2)
g = 2*x*y # Gradient.
lg = 2 * x # Gradient on a log scale.
plt.clf()
plt.plot(x, y)
plt.yscale('log')
for x in [0,2,4,7,18]:
angle_data = np.rad2deg(np.arctan2(2 * x * np.exp(x**2), 1))
y = np.exp(x**2)
angle_screen = plt.gca().transData.transform_angles(np.array((angle_data,)), np.array([x, y]).reshape((1, 2)))[0]
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=angle_screen, horizontalalignment='center')
plt.ylim(1e0, 1e180)
plt.xlim(-1, 20)
plt.xlabel(r'$x$')
plt.title(r'$exp(x^2)$', y=1.05)
plt.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
A few ideas?
I had tried to use the fact that for very large functions I can calculate the difference from 90 degrees using arctan(x) ~ pi/2 - arctan(1/x), and the former angle uses the low angle approximation so is just 1/x. However, after plugging this into transform_angles
this is rounded incorrectly.
A slight hack of a solution
If I guess the aspect ratio of the figure (c0.6) and then also adjust for the difference in scales (x in [0:20]
while log10(y) is in [0:180]
, giving a difference of 9 in scale), then I can get the following, although I don't think this is particularly sustainable, especially if I want to tweak something later.
# The 9 comes from tha fact that x is in [0:20], log10(y) is in [0, 180]. The factor of 0.6 is roughly the aspect ratio of the main plot shape.
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=np.rad2deg(np.arctan(0.6 * x/9.0)), horizontalalignment='center')
python matplotlib rotation logarithm
The problem is that the angle in data coordinates is essentially 90 degrees for those Texts in question.
– ImportanceOfBeingErnest
Nov 22 '18 at 16:30
Correct, and frustratingly I know what the gradient should be on the log scale.
– oliversm
Nov 22 '18 at 16:32
I suppose using angles viaarctan
is not possible here. I didn't look into the otjher answers to the linked question too deeply; maybe they are better suited for your case?
– ImportanceOfBeingErnest
Nov 22 '18 at 16:37
add a comment |
I am trying to have text rotate onto a plot which is shown on log scale. When I compute the angles (based on the solution in this answer) the angles are getting incorrectly rounded to 0 or 90 degrees. This is because the angles are computed on a linear scale first, and then transformed. This calculation in linear space is the cause of the trouble. Even in a situation where I know the gradient, (either in a linear or logarithmic scale), I am not sure how I can put this onto the graph correctly.
MWE
import matplotlib as mpl
rc_fonts = {
"text.usetex": True,
'text.latex.preview': True,
"font.size": 50,
'mathtext.default': 'regular',
'axes.titlesize': 55,
"axes.labelsize": 55,
"legend.fontsize": 50,
"xtick.labelsize": 50,
"ytick.labelsize": 50,
'figure.titlesize': 55,
'figure.figsize': (10, 6.5), # 15, 9.3
'text.latex.preamble': [
r"""usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}
"""],
"font.family": "serif",
"font.serif": "computer modern roman",
}
mpl.rcParams.update(rc_fonts)
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, InsetPosition, mark_inset
import numpy as np
x = np.linspace(0, 20, 100)
y = np.exp(x**2)
g = 2*x*y # Gradient.
lg = 2 * x # Gradient on a log scale.
plt.clf()
plt.plot(x, y)
plt.yscale('log')
for x in [0,2,4,7,18]:
angle_data = np.rad2deg(np.arctan2(2 * x * np.exp(x**2), 1))
y = np.exp(x**2)
angle_screen = plt.gca().transData.transform_angles(np.array((angle_data,)), np.array([x, y]).reshape((1, 2)))[0]
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=angle_screen, horizontalalignment='center')
plt.ylim(1e0, 1e180)
plt.xlim(-1, 20)
plt.xlabel(r'$x$')
plt.title(r'$exp(x^2)$', y=1.05)
plt.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
A few ideas?
I had tried to use the fact that for very large functions I can calculate the difference from 90 degrees using arctan(x) ~ pi/2 - arctan(1/x), and the former angle uses the low angle approximation so is just 1/x. However, after plugging this into transform_angles
this is rounded incorrectly.
A slight hack of a solution
If I guess the aspect ratio of the figure (c0.6) and then also adjust for the difference in scales (x in [0:20]
while log10(y) is in [0:180]
, giving a difference of 9 in scale), then I can get the following, although I don't think this is particularly sustainable, especially if I want to tweak something later.
# The 9 comes from tha fact that x is in [0:20], log10(y) is in [0, 180]. The factor of 0.6 is roughly the aspect ratio of the main plot shape.
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=np.rad2deg(np.arctan(0.6 * x/9.0)), horizontalalignment='center')
python matplotlib rotation logarithm
I am trying to have text rotate onto a plot which is shown on log scale. When I compute the angles (based on the solution in this answer) the angles are getting incorrectly rounded to 0 or 90 degrees. This is because the angles are computed on a linear scale first, and then transformed. This calculation in linear space is the cause of the trouble. Even in a situation where I know the gradient, (either in a linear or logarithmic scale), I am not sure how I can put this onto the graph correctly.
MWE
import matplotlib as mpl
rc_fonts = {
"text.usetex": True,
'text.latex.preview': True,
"font.size": 50,
'mathtext.default': 'regular',
'axes.titlesize': 55,
"axes.labelsize": 55,
"legend.fontsize": 50,
"xtick.labelsize": 50,
"ytick.labelsize": 50,
'figure.titlesize': 55,
'figure.figsize': (10, 6.5), # 15, 9.3
'text.latex.preamble': [
r"""usepackage{lmodern,amsmath,amssymb,bm,physics,mathtools,nicefrac,letltxmacro,fixcmex}
"""],
"font.family": "serif",
"font.serif": "computer modern roman",
}
mpl.rcParams.update(rc_fonts)
import matplotlib.pylab as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, InsetPosition, mark_inset
import numpy as np
x = np.linspace(0, 20, 100)
y = np.exp(x**2)
g = 2*x*y # Gradient.
lg = 2 * x # Gradient on a log scale.
plt.clf()
plt.plot(x, y)
plt.yscale('log')
for x in [0,2,4,7,18]:
angle_data = np.rad2deg(np.arctan2(2 * x * np.exp(x**2), 1))
y = np.exp(x**2)
angle_screen = plt.gca().transData.transform_angles(np.array((angle_data,)), np.array([x, y]).reshape((1, 2)))[0]
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=angle_screen, horizontalalignment='center')
plt.ylim(1e0, 1e180)
plt.xlim(-1, 20)
plt.xlabel(r'$x$')
plt.title(r'$exp(x^2)$', y=1.05)
plt.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
A few ideas?
I had tried to use the fact that for very large functions I can calculate the difference from 90 degrees using arctan(x) ~ pi/2 - arctan(1/x), and the former angle uses the low angle approximation so is just 1/x. However, after plugging this into transform_angles
this is rounded incorrectly.
A slight hack of a solution
If I guess the aspect ratio of the figure (c0.6) and then also adjust for the difference in scales (x in [0:20]
while log10(y) is in [0:180]
, giving a difference of 9 in scale), then I can get the following, although I don't think this is particularly sustainable, especially if I want to tweak something later.
# The 9 comes from tha fact that x is in [0:20], log10(y) is in [0, 180]. The factor of 0.6 is roughly the aspect ratio of the main plot shape.
plt.gca().text(x, y, r'A', rotation_mode='anchor', rotation=np.rad2deg(np.arctan(0.6 * x/9.0)), horizontalalignment='center')
python matplotlib rotation logarithm
python matplotlib rotation logarithm
edited Nov 22 '18 at 17:05
oliversm
asked Nov 22 '18 at 15:56
oliversmoliversm
3381522
3381522
The problem is that the angle in data coordinates is essentially 90 degrees for those Texts in question.
– ImportanceOfBeingErnest
Nov 22 '18 at 16:30
Correct, and frustratingly I know what the gradient should be on the log scale.
– oliversm
Nov 22 '18 at 16:32
I suppose using angles viaarctan
is not possible here. I didn't look into the otjher answers to the linked question too deeply; maybe they are better suited for your case?
– ImportanceOfBeingErnest
Nov 22 '18 at 16:37
add a comment |
The problem is that the angle in data coordinates is essentially 90 degrees for those Texts in question.
– ImportanceOfBeingErnest
Nov 22 '18 at 16:30
Correct, and frustratingly I know what the gradient should be on the log scale.
– oliversm
Nov 22 '18 at 16:32
I suppose using angles viaarctan
is not possible here. I didn't look into the otjher answers to the linked question too deeply; maybe they are better suited for your case?
– ImportanceOfBeingErnest
Nov 22 '18 at 16:37
The problem is that the angle in data coordinates is essentially 90 degrees for those Texts in question.
– ImportanceOfBeingErnest
Nov 22 '18 at 16:30
The problem is that the angle in data coordinates is essentially 90 degrees for those Texts in question.
– ImportanceOfBeingErnest
Nov 22 '18 at 16:30
Correct, and frustratingly I know what the gradient should be on the log scale.
– oliversm
Nov 22 '18 at 16:32
Correct, and frustratingly I know what the gradient should be on the log scale.
– oliversm
Nov 22 '18 at 16:32
I suppose using angles via
arctan
is not possible here. I didn't look into the otjher answers to the linked question too deeply; maybe they are better suited for your case?– ImportanceOfBeingErnest
Nov 22 '18 at 16:37
I suppose using angles via
arctan
is not possible here. I didn't look into the otjher answers to the linked question too deeply; maybe they are better suited for your case?– ImportanceOfBeingErnest
Nov 22 '18 at 16:37
add a comment |
1 Answer
1
active
oldest
votes
I updated the solution to the original question with a class RotationAwareAnnotation2
, which will be better suited here. It would first transform the points into screen coordinates, and then apply the rotation.
This this case it would look as follows.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.text as mtext
import matplotlib.transforms as mtransforms
class RotationAwareAnnotation2(mtext.Annotation):
def __init__(self, s, xy, p, pa=None, ax=None, **kwargs):
self.ax = ax or plt.gca()
self.p = p
if not pa:
self.pa = xy
kwargs.update(rotation_mode=kwargs.get("rotation_mode", "anchor"))
mtext.Annotation.__init__(self, s, xy, **kwargs)
self.set_transform(mtransforms.IdentityTransform())
if 'clip_on' in kwargs:
self.set_clip_path(self.ax.patch)
self.ax._add_text(self)
def calc_angle(self):
p = self.ax.transData.transform_point(self.p)
pa = self.ax.transData.transform_point(self.pa)
ang = np.arctan2(p[1]-pa[1], p[0]-pa[0])
return np.rad2deg(ang)
def _get_rotation(self):
return self.calc_angle()
def _set_rotation(self, rotation):
pass
_rotation = property(_get_rotation, _set_rotation)
x = np.linspace(0, 20, 100)
f = lambda x: np.exp(x**2)
y = f(x)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(yscale = 'log', ylim=(1e0, 1e180), xlim=(-1, 20), xlabel=r'$x$')
annots=
for xi in [0,2,4,7,18]:
an = RotationAwareAnnotation2("A", xy=(xi,f(xi)), p=(xi+.01,f(xi+.01)), ax=ax,
xytext=(-1,1), textcoords="offset points",
ha="center", va="baseline", fontsize=40)
annots.append(an)
ax.set_title(r'$exp(x^2)$', y=1.05)
fig.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
plt.show()
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53434558%2fmatplotlib-rotating-text-on-log-scale-where-angles-are-incorrectly-rounded%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
I updated the solution to the original question with a class RotationAwareAnnotation2
, which will be better suited here. It would first transform the points into screen coordinates, and then apply the rotation.
This this case it would look as follows.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.text as mtext
import matplotlib.transforms as mtransforms
class RotationAwareAnnotation2(mtext.Annotation):
def __init__(self, s, xy, p, pa=None, ax=None, **kwargs):
self.ax = ax or plt.gca()
self.p = p
if not pa:
self.pa = xy
kwargs.update(rotation_mode=kwargs.get("rotation_mode", "anchor"))
mtext.Annotation.__init__(self, s, xy, **kwargs)
self.set_transform(mtransforms.IdentityTransform())
if 'clip_on' in kwargs:
self.set_clip_path(self.ax.patch)
self.ax._add_text(self)
def calc_angle(self):
p = self.ax.transData.transform_point(self.p)
pa = self.ax.transData.transform_point(self.pa)
ang = np.arctan2(p[1]-pa[1], p[0]-pa[0])
return np.rad2deg(ang)
def _get_rotation(self):
return self.calc_angle()
def _set_rotation(self, rotation):
pass
_rotation = property(_get_rotation, _set_rotation)
x = np.linspace(0, 20, 100)
f = lambda x: np.exp(x**2)
y = f(x)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(yscale = 'log', ylim=(1e0, 1e180), xlim=(-1, 20), xlabel=r'$x$')
annots=
for xi in [0,2,4,7,18]:
an = RotationAwareAnnotation2("A", xy=(xi,f(xi)), p=(xi+.01,f(xi+.01)), ax=ax,
xytext=(-1,1), textcoords="offset points",
ha="center", va="baseline", fontsize=40)
annots.append(an)
ax.set_title(r'$exp(x^2)$', y=1.05)
fig.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
plt.show()
add a comment |
I updated the solution to the original question with a class RotationAwareAnnotation2
, which will be better suited here. It would first transform the points into screen coordinates, and then apply the rotation.
This this case it would look as follows.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.text as mtext
import matplotlib.transforms as mtransforms
class RotationAwareAnnotation2(mtext.Annotation):
def __init__(self, s, xy, p, pa=None, ax=None, **kwargs):
self.ax = ax or plt.gca()
self.p = p
if not pa:
self.pa = xy
kwargs.update(rotation_mode=kwargs.get("rotation_mode", "anchor"))
mtext.Annotation.__init__(self, s, xy, **kwargs)
self.set_transform(mtransforms.IdentityTransform())
if 'clip_on' in kwargs:
self.set_clip_path(self.ax.patch)
self.ax._add_text(self)
def calc_angle(self):
p = self.ax.transData.transform_point(self.p)
pa = self.ax.transData.transform_point(self.pa)
ang = np.arctan2(p[1]-pa[1], p[0]-pa[0])
return np.rad2deg(ang)
def _get_rotation(self):
return self.calc_angle()
def _set_rotation(self, rotation):
pass
_rotation = property(_get_rotation, _set_rotation)
x = np.linspace(0, 20, 100)
f = lambda x: np.exp(x**2)
y = f(x)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(yscale = 'log', ylim=(1e0, 1e180), xlim=(-1, 20), xlabel=r'$x$')
annots=
for xi in [0,2,4,7,18]:
an = RotationAwareAnnotation2("A", xy=(xi,f(xi)), p=(xi+.01,f(xi+.01)), ax=ax,
xytext=(-1,1), textcoords="offset points",
ha="center", va="baseline", fontsize=40)
annots.append(an)
ax.set_title(r'$exp(x^2)$', y=1.05)
fig.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
plt.show()
add a comment |
I updated the solution to the original question with a class RotationAwareAnnotation2
, which will be better suited here. It would first transform the points into screen coordinates, and then apply the rotation.
This this case it would look as follows.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.text as mtext
import matplotlib.transforms as mtransforms
class RotationAwareAnnotation2(mtext.Annotation):
def __init__(self, s, xy, p, pa=None, ax=None, **kwargs):
self.ax = ax or plt.gca()
self.p = p
if not pa:
self.pa = xy
kwargs.update(rotation_mode=kwargs.get("rotation_mode", "anchor"))
mtext.Annotation.__init__(self, s, xy, **kwargs)
self.set_transform(mtransforms.IdentityTransform())
if 'clip_on' in kwargs:
self.set_clip_path(self.ax.patch)
self.ax._add_text(self)
def calc_angle(self):
p = self.ax.transData.transform_point(self.p)
pa = self.ax.transData.transform_point(self.pa)
ang = np.arctan2(p[1]-pa[1], p[0]-pa[0])
return np.rad2deg(ang)
def _get_rotation(self):
return self.calc_angle()
def _set_rotation(self, rotation):
pass
_rotation = property(_get_rotation, _set_rotation)
x = np.linspace(0, 20, 100)
f = lambda x: np.exp(x**2)
y = f(x)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(yscale = 'log', ylim=(1e0, 1e180), xlim=(-1, 20), xlabel=r'$x$')
annots=
for xi in [0,2,4,7,18]:
an = RotationAwareAnnotation2("A", xy=(xi,f(xi)), p=(xi+.01,f(xi+.01)), ax=ax,
xytext=(-1,1), textcoords="offset points",
ha="center", va="baseline", fontsize=40)
annots.append(an)
ax.set_title(r'$exp(x^2)$', y=1.05)
fig.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
plt.show()
I updated the solution to the original question with a class RotationAwareAnnotation2
, which will be better suited here. It would first transform the points into screen coordinates, and then apply the rotation.
This this case it would look as follows.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.text as mtext
import matplotlib.transforms as mtransforms
class RotationAwareAnnotation2(mtext.Annotation):
def __init__(self, s, xy, p, pa=None, ax=None, **kwargs):
self.ax = ax or plt.gca()
self.p = p
if not pa:
self.pa = xy
kwargs.update(rotation_mode=kwargs.get("rotation_mode", "anchor"))
mtext.Annotation.__init__(self, s, xy, **kwargs)
self.set_transform(mtransforms.IdentityTransform())
if 'clip_on' in kwargs:
self.set_clip_path(self.ax.patch)
self.ax._add_text(self)
def calc_angle(self):
p = self.ax.transData.transform_point(self.p)
pa = self.ax.transData.transform_point(self.pa)
ang = np.arctan2(p[1]-pa[1], p[0]-pa[0])
return np.rad2deg(ang)
def _get_rotation(self):
return self.calc_angle()
def _set_rotation(self, rotation):
pass
_rotation = property(_get_rotation, _set_rotation)
x = np.linspace(0, 20, 100)
f = lambda x: np.exp(x**2)
y = f(x)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(yscale = 'log', ylim=(1e0, 1e180), xlim=(-1, 20), xlabel=r'$x$')
annots=
for xi in [0,2,4,7,18]:
an = RotationAwareAnnotation2("A", xy=(xi,f(xi)), p=(xi+.01,f(xi+.01)), ax=ax,
xytext=(-1,1), textcoords="offset points",
ha="center", va="baseline", fontsize=40)
annots.append(an)
ax.set_title(r'$exp(x^2)$', y=1.05)
fig.savefig('logscale.pdf', format='pdf', bbox_inches='tight')
plt.show()
answered Nov 24 '18 at 23:08
ImportanceOfBeingErnestImportanceOfBeingErnest
128k12136212
128k12136212
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53434558%2fmatplotlib-rotating-text-on-log-scale-where-angles-are-incorrectly-rounded%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
The problem is that the angle in data coordinates is essentially 90 degrees for those Texts in question.
– ImportanceOfBeingErnest
Nov 22 '18 at 16:30
Correct, and frustratingly I know what the gradient should be on the log scale.
– oliversm
Nov 22 '18 at 16:32
I suppose using angles via
arctan
is not possible here. I didn't look into the otjher answers to the linked question too deeply; maybe they are better suited for your case?– ImportanceOfBeingErnest
Nov 22 '18 at 16:37