Matplotlib artist should stay the same size when zoomed in, but ALSO move with panning?

This is a very direct observation of this issue .

Using matplotlib , I would like to place some kind of "highlight bar" above the range of data markers, which, as I know, will be in a straight horizontal line.

This bar / rectangle should be slightly above the markers and contain them, something like this for the three markers below:

enter image description here

To be a reasonable backlight panel, it must have the following two features:

  • If the graph is shaded, the panel moves along with the markers (therefore, it always covers them).
  • If the graph is enlarged, the display height of the panel does not change (therefore, it is always slightly higher than the markers).

If it’s useful to know, these markers do not have significant y values ​​(they are built all on y = -1), only significant x values. Therefore, the height of the bar does not make sense in the coordinates of the data; it just needs to always be tall enough to enclose the markers.

+6
source share
1 answer

Great question! It was a good challenge, and a combination of things is required to achieve this.

First, we need to come up with a transformation that returns the device coordinates of a predetermined value plus an offset based on this point. For example, if we know that we want the string to be in x_pt, y_pt, then the transformation should represent (in pseudocode):

def transform(x, y): return x_pt_in_device + x, y_pt_in_device + y 

Once we do this, we can use this transformation to draw a 20px rectangle around a fixed data point. However, you only want to draw a rectangle with a fixed pixel height in the y direction, but in the x direction you would like standard data scaling.

Therefore, we need to create a mixed transformation that can independently transform the x and y coordinates. All code to do what you ask:

 import matplotlib.pyplot as plt import matplotlib.patches as mpatches import matplotlib.path as mpath import matplotlib.transforms as mtrans import numpy as np class FixedPointOffsetTransform(mtrans.Transform): """ Always returns the same transformed point plus the given point in device coordinates as an offset. """ def __init__(self, trans, fixed_point): mtrans.Transform.__init__(self) self.input_dims = self.output_dims = 2 self.trans = trans self.fixed_point = np.array(fixed_point).reshape(1, 2) def transform(self, values): fp = self.trans.transform(self.fixed_point) values = np.array(values) if values.ndim == 1: return fp.flatten() + values else: return fp + values plt.scatter([3.1, 3.2, 3.4, 5], [2, 2, 2, 6]) ax = plt.gca() fixed_pt_trans = FixedPointOffsetTransform(ax.transData, (0, 2)) xdata_yfixed = mtrans.blended_transform_factory(ax.transData, fixed_pt_trans) x = [3.075, 3.425] # x range of box (in data coords) height = 20 # of box in device coords (pixels) path = mpath.Path([[x[0], -height], [x[1], -height], [x[1], height], [x[0], height], [x[0], -height]]) patch = mpatches.PathPatch(path, transform=xdata_yfixed, facecolor='red', edgecolor='black', alpha=0.4, zorder=0) ax.add_patch(patch) plt.show() 
+6
source

Source: https://habr.com/ru/post/911355/


All Articles