Skip to content

Commit d558e2d

Browse files
authored
Add info after scale bar has been drawn (#67)
1 parent 5ff1b02 commit d558e2d

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,35 @@ ax.add_artist(scalebar)
108108
fig.savefig("original_resolution.png", dpi=dpi)
109109
```
110110

111+
### Information about the drawn scale bar
112+
113+
After the scale bar has been drawn, the `info` property returns the following dataclass.
114+
115+
```python
116+
@dataclasses.dataclass
117+
class ScaleBarInfo:
118+
length_px: int
119+
value: float
120+
units: str
121+
scale_text: str
122+
window_extent: matplotlib.transforms.Bbox
123+
```
124+
125+
Note that the `info` property returns a `ValueError` exception if the scale bar has not been drawn.
126+
127+
```python
128+
fig, ax = plt.subplots()
129+
130+
scalebar = ScaleBar(0.08, "cm", length_fraction=0.25)
131+
ax.add_artist(scalebar)
132+
133+
print(scalebar.info) # raises a ValueError exception
134+
135+
fig.canvas.draw()
136+
137+
print(scalebar.info) # works
138+
```
139+
111140
## ScaleBar arguments
112141

113142
Here are arguments of the **ScaleBar** class constructor and examples how to use them.

matplotlib_scalebar/scalebar.py

+23
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
# Standard library modules.
4141
import bisect
4242
import warnings
43+
import dataclasses
4344

4445
# Third party modules.
4546
import matplotlib
@@ -142,6 +143,15 @@ def _validate_legend_loc(loc):
142143
}
143144

144145

146+
@dataclasses.dataclass
147+
class ScaleBarInfo:
148+
length_px: int
149+
value: float
150+
units: str
151+
scale_text: str
152+
window_extent: matplotlib.transforms.Bbox
153+
154+
145155
class ScaleBar(Artist):
146156
zorder = 6
147157

@@ -368,6 +378,7 @@ def __init__(
368378
self.rotation = rotation
369379
self.bbox_to_anchor = bbox_to_anchor
370380
self.bbox_transform = bbox_transform
381+
self._info = None
371382

372383
def _calculate_best_length(self, length_px):
373384
dx = self.dx
@@ -393,6 +404,8 @@ def _calculate_exact_length(self, value, units):
393404
return newvalue / self.dx
394405

395406
def draw(self, renderer, *args, **kwargs):
407+
self._info = None
408+
396409
if not self.get_visible():
397410
return
398411
if self.dx == 0:
@@ -555,6 +568,10 @@ def _get_value(attr, default):
555568
box.patch.set_alpha(box_alpha)
556569
box.draw(renderer)
557570

571+
self._info = ScaleBarInfo(
572+
length_px, value, units, scale_text, box.get_window_extent(renderer)
573+
)
574+
558575
def get_dx(self):
559576
return self._dx
560577

@@ -841,3 +858,9 @@ def set_bbox_transform(self, bbox_transform):
841858
self._bbox_transform = bbox_transform
842859

843860
bbox_transform = property(get_bbox_transform, set_bbox_transform)
861+
862+
@property
863+
def info(self):
864+
if self._info is None:
865+
raise ValueError("Scale bar has not been drawn. Call figure.canvas.draw()")
866+
return self._info

tests/test_scalebar.py

+29
Original file line numberDiff line numberDiff line change
@@ -369,3 +369,32 @@ def test_bbox_transform(scalebar):
369369
scalebar.bbox_transform = scalebar.axes.transAxes
370370
assert scalebar.get_bbox_transform() == scalebar.axes.transAxes
371371
assert scalebar.bbox_transform == scalebar.axes.transAxes
372+
373+
374+
def test_info():
375+
fig = plt.figure()
376+
ax = fig.add_subplot(111)
377+
378+
data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
379+
ax.imshow(data)
380+
381+
scalebar = ScaleBar(0.5)
382+
ax.add_artist(scalebar)
383+
384+
with pytest.raises(ValueError):
385+
scalebar.info
386+
387+
plt.draw()
388+
389+
info = scalebar.info
390+
assert info.length_px == pytest.approx(0.4, 1e-4)
391+
assert info.value == pytest.approx(2, 1e-4)
392+
assert info.units == "dm"
393+
assert info.scale_text == "2 dm"
394+
assert info.window_extent.x0 == pytest.approx(456.5755555555555, 1e-4)
395+
assert info.window_extent.y0 == pytest.approx(390.81511111111104, 1e-4)
396+
assert info.window_extent.x1 == pytest.approx(511.4111111111111, 1e-4)
397+
assert info.window_extent.y1 == pytest.approx(421.01111111111106, 1e-4)
398+
399+
plt.close()
400+
del fig

0 commit comments

Comments
 (0)