pyxem / orix

Analysing crystal orientations and symmetry in Python
https://orix.readthedocs.io
GNU General Public License v3.0
81 stars 49 forks source link

Adding text on a plot #422

Open maclariz opened 1 year ago

maclariz commented 1 year ago

I cannot seem to add text within a stereographic plot. Here is the code:

axh = plt.subplot(gs[2,2:4], projection='stereographic')
axh.scatter(v_alpha2_pruned, ec='k', linewidths=0.5, c=labels_rgb_pruned, 
                     alpha=0.7, s=120, marker='o')
axh.text(Vector3d.from_polar(1,1),letter[7], size=20)

The errors said the position had to be defined as a Vector3d. I did this, and still it fails:

TypeError                                 Traceback (most recent call last)
Input In [200], in <cell line: 56>()
     53 axh = plt.subplot(gs[2,2:4], projection='stereographic')
     54 axh.scatter(v_alpha2_pruned, ec='k', linewidths=0.5, c=labels_rgb_pruned, 
     55                      alpha=0.7, s=120, marker='o')
---> 56 axh.text(Vector3d.from_polar(1,1),letter[7], size=20)
     58 axi = plt.subplot(gs[2,4:6], projection='plot_map')
     59 axi.plot_map(Timap["Alpha Titanium"], labels_rgb, scalebar_properties={"location": 4})

File /local/environments/kernel/kernel-venv/lib64/python3.8/site-packages/orix/plot/stereographic_plot.py:238, in StereographicPlot.text(self, *args, **kwargs)
    219 """Add text to the axes.
    220 
    221 This method overwrites :meth:`matplotlib.axes.Axes.text`, see
   (...)
    235 matplotlib.axes.Axes.text
    236 """
    237 new_kwargs = dict(va="bottom", ha="center", zorder=ZORDER["text"])
--> 238 out = self._prepare_to_call_inherited_method(args, kwargs, new_kwargs)
    239 x, y, _, updated_kwargs = out
    240 if x.size == 0:

File /local/environments/kernel/kernel-venv/lib64/python3.8/site-packages/orix/plot/stereographic_plot.py:757, in StereographicPlot._prepare_to_call_inherited_method(self, args, kwargs, new_kwargs, sort)
    755     for k, v in new_kwargs.items():
    756         updated_kwargs.setdefault(k, v)
--> 757 x, y, visible = self._pretransform_input(args, sort=sort)
    758 return x, y, visible, updated_kwargs

File /local/environments/kernel/kernel-venv/lib64/python3.8/site-packages/orix/plot/stereographic_plot.py:792, in StereographicPlot._pretransform_input(self, values, sort)
    790         azimuth = azimuth[order]
    791         polar = polar[order]
--> 792     x, y = self._projection.spherical2xy(azimuth=azimuth, polar=polar)
    793     v = self._inverse_projection.xy2vector(x, y)
    794 else:

File /local/environments/kernel/kernel-venv/lib64/python3.8/site-packages/orix/projections/stereographic.py:117, in StereographicProjection.spherical2xy(self, azimuth, polar)
     85 def spherical2xy(
     86     self, azimuth: Union[float, np.ndarray], polar: Union[float, np.ndarray]
     87 ) -> Tuple[np.ndarray, np.ndarray]:
     88     r"""Return stereographic coordinates (X, Y) from 3D unit vectors
     89     created from spherical coordinates, azimuth :math:`\phi` and
     90     polar :math:`\theta`, defined as in the ISO 31-11 standard
   (...)
    115     vector2xy
    116     """
--> 117     v = Vector3d.from_polar(azimuth=azimuth, polar=polar)
    118     return self.vector2xy(v)

File /local/environments/kernel/kernel-venv/lib64/python3.8/site-packages/orix/vector/vector3d.py:431, in Vector3d.from_polar(cls, azimuth, polar, radial)
    429 azimuth = np.atleast_1d(azimuth)
    430 polar = np.atleast_1d(polar)
--> 431 sin_polar = np.sin(polar)
    432 x = np.cos(azimuth) * sin_polar
    433 y = np.sin(azimuth) * sin_polar

TypeError: ufunc 'sin' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
hakonanes commented 1 year ago

axh.text(Vector3d.from_polar(1,1),letter[7], size=20)

What is letter[7] here? I think you have to specify it as a keyword argument s=letter[7].

hakonanes commented 1 year ago

I must say not being able to pass cartesian (x, y) to StereographicPlot.text() has annoyed me sometimes. One solution is to "bypass" our custom text() method (docs) when x and y are among the keyword arguments, and use Matplotlib directly to place a piece of text. I think this is clean and straight forward.

maclariz commented 1 year ago

Okay, your workaround works, you do not normally need to tell "text" that a text string is the text. i.e. this code worked in all other cells that weren't stereographic... It not being cartesian and not being allowed to specify transform=axh.transAxes is annoying but easy to work around

hakonanes commented 1 year ago

you do not normally need to tell "text" that a text string is the text. i.e. this code worked in all other cells that weren't stereographic...

You are right, with the StereographicPlot we have overwritten Axes.plot(), scatter() and text() to allow passing a Vector3d or azimuthal and polar angles (see e.g. the scatter() docs). To separate between these two types of input, our overridden methods require all other arguments passed on to Matplotlib to be keyword arguments. This is stated in the docstrings. I don't see a way to have this functionality and still allow a string to be the second argument (not keyword argument) passed.

maclariz commented 1 year ago

It's fine. Just confusing first time.

hakonanes commented 1 year ago

I suggest to leave this open until we've either implemented my suggestion in https://github.com/pyxem/orix/issues/422#issuecomment-1410577422 or have decided it's not a good idea.

Thank you for opening this issue, @maclariz.

maclariz commented 1 year ago

I will also comment that you did not touch ax.set_title, so this can still be used. For what I was doing (adding letters a)-h) for a multipart figure), this was fine, as I only needed something in top left, so could use:

ax.set_title(letter[i], loc='left')

hakonanes commented 1 year ago

Just realized we haven't overwritten Figure.text(). We can use this method to add text as normal with Matplotlib.