Matplotlib - rotating text on log scale where angles are incorrectly rounded












1















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



enter image description here



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.



enter image description here



# 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')









share|improve this question

























  • 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
















1















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



enter image description here



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.



enter image description here



# 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')









share|improve this question

























  • 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














1












1








1








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



enter image description here



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.



enter image description here



# 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')









share|improve this question
















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



enter image description here



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.



enter image description here



# 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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 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



















  • 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

















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












1 Answer
1






active

oldest

votes


















1














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()


enter image description here






share|improve this answer























    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
    });


    }
    });














    draft saved

    draft discarded


















    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









    1














    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()


    enter image description here






    share|improve this answer




























      1














      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()


      enter image description here






      share|improve this answer


























        1












        1








        1







        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()


        enter image description here






        share|improve this answer













        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()


        enter image description here







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 24 '18 at 23:08









        ImportanceOfBeingErnestImportanceOfBeingErnest

        128k12136212




        128k12136212






























            draft saved

            draft discarded




















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

            Calculate evaluation metrics using cross_val_predict sklearn

            Insert data from modal to MySQL (multiple modal on website)