faceted 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,696 @@
1
+ """Test suite for faceted module"""
2
+
3
+ from collections import OrderedDict
4
+ from itertools import product
5
+
6
+ import matplotlib.axes
7
+ import matplotlib.figure
8
+ import matplotlib.pyplot as plt
9
+ import numpy as np
10
+ import pytest
11
+
12
+ from ..faceted import (
13
+ _DEFAULT_ASPECT,
14
+ _DEFAULT_WIDTH,
15
+ faceted,
16
+ faceted_ax,
17
+ _infer_constraints,
18
+ _infer_grid_class,
19
+ HeightConstrainedAxesGrid,
20
+ HeightAndWidthConstrainedAxesGrid,
21
+ WidthConstrainedAxesGrid,
22
+ )
23
+
24
+
25
+ plt.switch_backend("agg")
26
+
27
+
28
+ _TOP_PAD = _BOTTOM_PAD = _LEFT_PAD = _RIGHT_PAD = 0.25
29
+ _HORIZONTAL_INTERNAL_PAD = 0.25
30
+ _VERTICAL_INTERNAL_PAD = 0.5
31
+ _INTERNAL_PAD = (_HORIZONTAL_INTERNAL_PAD, _VERTICAL_INTERNAL_PAD)
32
+ _ASPECT_CONSTRAINT = 0.5
33
+ _HEIGHT_CONSTRAINT = 7.0
34
+ _WIDTH_CONSTRAINT = 8.0
35
+ _SHORT_SIDE_PAD = 0.25
36
+ _LONG_SIDE_PAD = 0.25
37
+ _CBAR_THICKNESS = 0.125
38
+
39
+
40
+ def get_bounds(fig, ax):
41
+ return ax.bbox.transformed(fig.transFigure.inverted()).bounds
42
+
43
+
44
+ def test_faceted_cbar_mode_none():
45
+ fig, axes = faceted(1, 2, width=_WIDTH_CONSTRAINT, aspect=_ASPECT_CONSTRAINT)
46
+ assert len(axes) == 2
47
+ plt.close(fig)
48
+
49
+
50
+ def test_faceted_cbar_mode_single():
51
+ fig, axes, cax = faceted(
52
+ 1, 2, width=_WIDTH_CONSTRAINT, aspect=_ASPECT_CONSTRAINT, cbar_mode="single"
53
+ )
54
+ assert len(axes) == 2
55
+ plt.close(fig)
56
+
57
+
58
+ def test_faceted_cbar_mode_each():
59
+ fig, axes, caxes = faceted(
60
+ 1, 2, width=_WIDTH_CONSTRAINT, aspect=_ASPECT_CONSTRAINT, cbar_mode="each"
61
+ )
62
+ assert len(axes) == 2
63
+ assert len(axes) == len(caxes)
64
+ plt.close(fig)
65
+
66
+
67
+ @pytest.mark.parametrize(
68
+ ("width", "height", "aspect"), [(1, 1, None), (1, None, 1), (None, 1, 1)]
69
+ )
70
+ def test_faceted_cbar_mode_invalid(width, height, aspect):
71
+ with pytest.raises(ValueError):
72
+ faceted(1, 2, width=width, height=height, aspect=aspect, cbar_mode="invalid")
73
+
74
+
75
+ def test_faceted_invalid_internal_pad():
76
+ with pytest.raises(ValueError):
77
+ faceted(
78
+ 1,
79
+ 2,
80
+ width=_WIDTH_CONSTRAINT,
81
+ aspect=_ASPECT_CONSTRAINT,
82
+ internal_pad=(1, 2, 3),
83
+ )
84
+
85
+
86
+ @pytest.mark.parametrize(
87
+ ("inputs", "expected"),
88
+ [
89
+ ((None, None, None), (_DEFAULT_WIDTH, None, _DEFAULT_ASPECT)),
90
+ ((3.0, None, None), (3.0, None, _DEFAULT_ASPECT)),
91
+ ((None, 3.0, None), (None, 3.0, _DEFAULT_ASPECT)),
92
+ ((None, None, 3.0), (_DEFAULT_WIDTH, None, 3.0)),
93
+ ((3.0, 3.0, None), (3.0, 3.0, None)),
94
+ ((None, 3.0, 3.0), (None, 3.0, 3.0)),
95
+ ((3.0, None, 3.0), (3.0, None, 3.0)),
96
+ ((3.0, 3.0, 3.0), ValueError),
97
+ ],
98
+ ids=lambda x: str(x),
99
+ )
100
+ def test__infer_constraints(inputs, expected):
101
+ if not isinstance(expected, tuple) and issubclass(expected, Exception):
102
+ with pytest.raises(expected):
103
+ _infer_constraints(*inputs)
104
+ else:
105
+ result = _infer_constraints(*inputs)
106
+ assert result == expected
107
+
108
+
109
+ @pytest.mark.parametrize(
110
+ ("width", "height", "aspect", "expected"),
111
+ [
112
+ (5.0, 5.0, None, HeightAndWidthConstrainedAxesGrid),
113
+ (5.0, None, 5.0, WidthConstrainedAxesGrid),
114
+ (None, 5.0, 5.0, HeightConstrainedAxesGrid),
115
+ ],
116
+ )
117
+ def test__infer_grid_class(width, height, aspect, expected):
118
+ result = _infer_grid_class(width, height, aspect)
119
+ assert result == expected
120
+
121
+
122
+ _LAYOUTS = [(1, 1), (1, 2), (2, 1), (2, 2), (5, 3)]
123
+ _CBAR_MODES = [None, "single", "each", "edge"]
124
+ _CBAR_LOCATIONS = ["bottom", "right", "top", "left"]
125
+ _CONSTRAINTS = ["height-and-aspect", "width-and-aspect", "height-and-width"]
126
+ _CG_LAYOUTS = product(_CBAR_MODES, _CBAR_LOCATIONS, _LAYOUTS, _CONSTRAINTS)
127
+
128
+
129
+ def format_layout(layout):
130
+ cbar_mode, cbar_loc, (rows, cols), constraint = layout
131
+ return "cbar_mode={!r}, cbar_location={!r}, rows={}, cols={}, constraint={}".format(
132
+ cbar_mode, cbar_loc, rows, cols, constraint
133
+ )
134
+
135
+
136
+ _CG_IDS = OrderedDict([(layout, format_layout(layout)) for layout in _CG_LAYOUTS])
137
+
138
+
139
+ @pytest.fixture(params=_CG_IDS.keys(), ids=_CG_IDS.values())
140
+ def grid(request):
141
+ mode, location, (rows, cols), constraint = request.param
142
+ if constraint == "width-and-aspect":
143
+ obj = WidthConstrainedAxesGrid(
144
+ rows,
145
+ cols,
146
+ width=_WIDTH_CONSTRAINT,
147
+ aspect=_ASPECT_CONSTRAINT,
148
+ top_pad=_TOP_PAD,
149
+ bottom_pad=_BOTTOM_PAD,
150
+ left_pad=_LEFT_PAD,
151
+ right_pad=_RIGHT_PAD,
152
+ cbar_mode=mode,
153
+ cbar_pad=_LONG_SIDE_PAD,
154
+ axes_pad=_INTERNAL_PAD,
155
+ cbar_location=location,
156
+ cbar_size=_CBAR_THICKNESS,
157
+ cbar_short_side_pad=_SHORT_SIDE_PAD,
158
+ )
159
+ elif constraint == "height-and-aspect":
160
+ obj = HeightConstrainedAxesGrid(
161
+ rows,
162
+ cols,
163
+ height=_HEIGHT_CONSTRAINT,
164
+ aspect=_ASPECT_CONSTRAINT,
165
+ top_pad=_TOP_PAD,
166
+ bottom_pad=_BOTTOM_PAD,
167
+ left_pad=_LEFT_PAD,
168
+ right_pad=_RIGHT_PAD,
169
+ cbar_mode=mode,
170
+ cbar_pad=_LONG_SIDE_PAD,
171
+ axes_pad=_INTERNAL_PAD,
172
+ cbar_location=location,
173
+ cbar_size=_CBAR_THICKNESS,
174
+ cbar_short_side_pad=_SHORT_SIDE_PAD,
175
+ )
176
+ elif constraint == "height-and-width":
177
+ obj = HeightAndWidthConstrainedAxesGrid(
178
+ rows,
179
+ cols,
180
+ height=_HEIGHT_CONSTRAINT,
181
+ width=_WIDTH_CONSTRAINT,
182
+ top_pad=_TOP_PAD,
183
+ bottom_pad=_BOTTOM_PAD,
184
+ left_pad=_LEFT_PAD,
185
+ right_pad=_RIGHT_PAD,
186
+ cbar_mode=mode,
187
+ cbar_pad=_LONG_SIDE_PAD,
188
+ axes_pad=_INTERNAL_PAD,
189
+ cbar_location=location,
190
+ cbar_size=_CBAR_THICKNESS,
191
+ cbar_short_side_pad=_SHORT_SIDE_PAD,
192
+ )
193
+ else:
194
+ raise NotImplementedError()
195
+ yield obj
196
+ plt.close(obj.fig)
197
+
198
+
199
+ def get_tile_width(grid, left_pad=_LEFT_PAD, right_pad=_RIGHT_PAD):
200
+ return (
201
+ grid.width - left_pad - right_pad - (grid.cols - 1) * _HORIZONTAL_INTERNAL_PAD
202
+ ) / grid.cols
203
+
204
+
205
+ def get_tile_height(grid, bottom_pad=_BOTTOM_PAD, top_pad=_TOP_PAD):
206
+ return (
207
+ grid.height - bottom_pad - top_pad - (grid.rows - 1) * _VERTICAL_INTERNAL_PAD
208
+ ) / grid.rows
209
+
210
+
211
+ def test_constrained_axes_positions(grid):
212
+ if grid.cbar_mode == "each":
213
+ check_constrained_axes_positions_each(grid)
214
+ elif grid.cbar_mode == "single":
215
+ check_constrained_axes_positions_single(grid)
216
+ elif grid.cbar_mode == "edge":
217
+ check_constrained_axes_positions_edge(grid)
218
+ elif grid.cbar_mode is None:
219
+ check_constrained_axes_positions_none(grid)
220
+
221
+
222
+ def test_constrained_caxes_positions(grid):
223
+ if grid.cbar_mode == "each":
224
+ check_constrained_caxes_positions_each(grid)
225
+ elif grid.cbar_mode == "single":
226
+ check_constrained_caxes_positions_single(grid)
227
+ elif grid.cbar_mode == "edge":
228
+ check_constrained_caxes_positions_edge(grid)
229
+ elif grid.cbar_mode is None:
230
+ pytest.skip("Skipping colorbar positions test, because cbar_mode=None")
231
+
232
+
233
+ def test_plot_aspect(grid):
234
+ fig = grid.fig
235
+ width, height = fig.get_size_inches()
236
+ for ax in grid.axes:
237
+ ax_bounds = get_bounds(fig, ax)
238
+ _, _, _plot_width, _plot_height = ax_bounds
239
+ plot_width = _plot_width * width
240
+ plot_height = _plot_height * height
241
+ expected = grid.aspect
242
+ result = plot_height / plot_width
243
+ np.testing.assert_allclose(result, expected)
244
+
245
+
246
+ def check_constrained_axes_positions_none(grid):
247
+ rows, cols = grid.rows, grid.cols
248
+ width, height = grid.width, grid.height
249
+ tile_width, tile_height = get_tile_width(grid), get_tile_height(grid)
250
+ fig = grid.fig
251
+
252
+ indexes = list(product(range(rows - 1, -1, -1), range(cols)))
253
+ for ax, (row, col) in zip(grid.axes, indexes):
254
+ ax_bounds = get_bounds(fig, ax)
255
+ x0 = (_LEFT_PAD + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)) / width
256
+ y0 = (_BOTTOM_PAD + row * (_VERTICAL_INTERNAL_PAD + tile_height)) / height
257
+ dx = tile_width / width
258
+ dy = tile_height / height
259
+ expected_bounds = [x0, y0, dx, dy]
260
+ np.testing.assert_allclose(ax_bounds, expected_bounds)
261
+
262
+
263
+ def check_constrained_axes_positions_single(grid):
264
+ rows, cols = grid.rows, grid.cols
265
+ width, height = grid.width, grid.height
266
+ cbar_location = grid.cbar_location
267
+ fig = grid.fig
268
+
269
+ left_pad, right_pad = _LEFT_PAD, _RIGHT_PAD
270
+ bottom_pad, top_pad = _BOTTOM_PAD, _TOP_PAD
271
+ if cbar_location == "left":
272
+ left_pad = _LEFT_PAD + _CBAR_THICKNESS + _LONG_SIDE_PAD
273
+ elif cbar_location == "right":
274
+ right_pad = _RIGHT_PAD + _CBAR_THICKNESS + _LONG_SIDE_PAD
275
+ elif cbar_location == "bottom":
276
+ bottom_pad = _BOTTOM_PAD + _CBAR_THICKNESS + _LONG_SIDE_PAD
277
+ elif cbar_location == "top":
278
+ top_pad = _TOP_PAD + _CBAR_THICKNESS + _LONG_SIDE_PAD
279
+
280
+ tile_width = get_tile_width(grid, left_pad=left_pad, right_pad=right_pad)
281
+ tile_height = get_tile_height(grid, bottom_pad=bottom_pad, top_pad=top_pad)
282
+
283
+ indexes = list(product(range(rows - 1, -1, -1), range(cols)))
284
+ axes = grid.axes
285
+ for ax, (row, col) in zip(axes, indexes):
286
+ ax_bounds = get_bounds(fig, ax)
287
+ x0 = (left_pad + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)) / width
288
+ y0 = (bottom_pad + row * (_VERTICAL_INTERNAL_PAD + tile_height)) / height
289
+ dx = tile_width / width
290
+ dy = tile_height / height
291
+ expected_bounds = [x0, y0, dx, dy]
292
+ np.testing.assert_allclose(ax_bounds, expected_bounds)
293
+
294
+
295
+ def check_constrained_caxes_positions_single(grid):
296
+ width, height = grid.width, grid.height
297
+ cbar_location = grid.cbar_location
298
+ fig = grid.fig
299
+
300
+ cax = grid.caxes
301
+ cax_bounds = get_bounds(fig, cax)
302
+ if cbar_location == "bottom":
303
+ x0 = (_LEFT_PAD + _SHORT_SIDE_PAD) / width
304
+ y0 = _BOTTOM_PAD / height
305
+ dx = (width - _LEFT_PAD - _RIGHT_PAD - 2.0 * _SHORT_SIDE_PAD) / width
306
+ dy = _CBAR_THICKNESS / height
307
+ elif cbar_location == "right":
308
+ x0 = (width - _CBAR_THICKNESS - _RIGHT_PAD) / width
309
+ y0 = (_BOTTOM_PAD + _SHORT_SIDE_PAD) / height
310
+ dx = _CBAR_THICKNESS / width
311
+ dy = (height - _TOP_PAD - _BOTTOM_PAD - 2.0 * _SHORT_SIDE_PAD) / height
312
+ elif cbar_location == "top":
313
+ x0 = (_LEFT_PAD + _SHORT_SIDE_PAD) / width
314
+ y0 = (height - _CBAR_THICKNESS - _TOP_PAD) / height
315
+ dx = (width - _LEFT_PAD - _RIGHT_PAD - 2.0 * _SHORT_SIDE_PAD) / width
316
+ dy = _CBAR_THICKNESS / height
317
+ elif cbar_location == "left":
318
+ x0 = _LEFT_PAD / width
319
+ y0 = (_BOTTOM_PAD + _SHORT_SIDE_PAD) / height
320
+ dx = _CBAR_THICKNESS / width
321
+ dy = (height - _TOP_PAD - _BOTTOM_PAD - 2.0 * _SHORT_SIDE_PAD) / height
322
+ expected_bounds = [x0, y0, dx, dy]
323
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
324
+
325
+
326
+ def check_constrained_axes_positions_each(grid):
327
+ rows, cols = grid.rows, grid.cols
328
+ width, height = grid.width, grid.height
329
+ tile_width, tile_height = get_tile_width(grid), get_tile_height(grid)
330
+ cbar_location = grid.cbar_location
331
+ fig = grid.fig
332
+
333
+ indexes = list(product(range(rows - 1, -1, -1), range(cols)))
334
+ axes = grid.axes
335
+ if cbar_location == "bottom":
336
+ for ax, (row, col) in zip(axes, indexes):
337
+ ax_bounds = get_bounds(fig, ax)
338
+ x0 = (_LEFT_PAD + col * (tile_width + _HORIZONTAL_INTERNAL_PAD)) / width
339
+ y0 = (
340
+ _BOTTOM_PAD
341
+ + _CBAR_THICKNESS
342
+ + _LONG_SIDE_PAD
343
+ + row * (tile_height + _VERTICAL_INTERNAL_PAD)
344
+ ) / height
345
+ dx = tile_width / width
346
+ dy = (tile_height - _CBAR_THICKNESS - _LONG_SIDE_PAD) / height
347
+ expected_bounds = [x0, y0, dx, dy]
348
+ np.testing.assert_allclose(ax_bounds, expected_bounds)
349
+ elif cbar_location == "top":
350
+ for ax, (row, col) in zip(axes, indexes):
351
+ ax_bounds = get_bounds(fig, ax)
352
+ x0 = (_LEFT_PAD + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)) / width
353
+ y0 = (_BOTTOM_PAD + row * (_VERTICAL_INTERNAL_PAD + tile_height)) / height
354
+ dx = tile_width / width
355
+ dy = (tile_height - _CBAR_THICKNESS - _LONG_SIDE_PAD) / height
356
+ expected_bounds = [x0, y0, dx, dy]
357
+ np.testing.assert_allclose(ax_bounds, expected_bounds)
358
+ elif cbar_location == "right":
359
+ for ax, (row, col) in zip(axes, indexes):
360
+ ax_bounds = get_bounds(fig, ax)
361
+ x0 = (_LEFT_PAD + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)) / width
362
+ y0 = (_BOTTOM_PAD + row * (_VERTICAL_INTERNAL_PAD + tile_height)) / height
363
+ dx = (tile_width - _CBAR_THICKNESS - _LONG_SIDE_PAD) / width
364
+ dy = tile_height / height
365
+ expected_bounds = [x0, y0, dx, dy]
366
+ np.testing.assert_allclose(ax_bounds, expected_bounds)
367
+ elif cbar_location == "left":
368
+ for ax, (row, col) in zip(axes, indexes):
369
+ ax_bounds = get_bounds(fig, ax)
370
+ x0 = (
371
+ _LEFT_PAD
372
+ + _CBAR_THICKNESS
373
+ + _LONG_SIDE_PAD
374
+ + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)
375
+ ) / width
376
+ y0 = (_BOTTOM_PAD + row * (_VERTICAL_INTERNAL_PAD + tile_height)) / height
377
+ dx = (tile_width - _CBAR_THICKNESS - _LONG_SIDE_PAD) / width
378
+ dy = tile_height / height
379
+ expected_bounds = [x0, y0, dx, dy]
380
+ np.testing.assert_allclose(ax_bounds, expected_bounds)
381
+
382
+
383
+ def check_constrained_caxes_positions_each(grid):
384
+ rows, cols = grid.rows, grid.cols
385
+ width, height = grid.width, grid.height
386
+ tile_width, tile_height = get_tile_width(grid), get_tile_height(grid)
387
+ cbar_location = grid.cbar_location
388
+ fig = grid.fig
389
+
390
+ indexes = list(product(range(rows - 1, -1, -1), range(cols)))
391
+ caxes = grid.caxes
392
+ if cbar_location == "bottom":
393
+ for cax, (row, col) in zip(caxes, indexes):
394
+ cax_bounds = get_bounds(fig, cax)
395
+ x0 = (
396
+ _LEFT_PAD
397
+ + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)
398
+ + _SHORT_SIDE_PAD
399
+ ) / width
400
+ y0 = (_BOTTOM_PAD + row * (_VERTICAL_INTERNAL_PAD + tile_height)) / height
401
+ dx = (tile_width - 2.0 * _SHORT_SIDE_PAD) / width
402
+ dy = _CBAR_THICKNESS / height
403
+ expected_bounds = [x0, y0, dx, dy]
404
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
405
+ elif cbar_location == "top":
406
+ for cax, (row, col) in zip(caxes, indexes):
407
+ cax_bounds = get_bounds(fig, cax)
408
+ x0 = (
409
+ _LEFT_PAD
410
+ + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)
411
+ + _SHORT_SIDE_PAD
412
+ ) / width
413
+ y0 = (
414
+ _BOTTOM_PAD
415
+ + row * (_VERTICAL_INTERNAL_PAD + tile_height)
416
+ + tile_height
417
+ - _CBAR_THICKNESS
418
+ ) / height
419
+ dx = (tile_width - 2.0 * _SHORT_SIDE_PAD) / width
420
+ dy = _CBAR_THICKNESS / height
421
+ expected_bounds = [x0, y0, dx, dy]
422
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
423
+ elif cbar_location == "right":
424
+ for cax, (row, col) in zip(caxes, indexes):
425
+ cax_bounds = get_bounds(fig, cax)
426
+ x0 = (
427
+ _LEFT_PAD
428
+ + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)
429
+ + tile_width
430
+ - _CBAR_THICKNESS
431
+ ) / width
432
+ y0 = (
433
+ _BOTTOM_PAD
434
+ + row * (_VERTICAL_INTERNAL_PAD + tile_height)
435
+ + _SHORT_SIDE_PAD
436
+ ) / height
437
+ dx = _CBAR_THICKNESS / width
438
+ dy = (tile_height - 2.0 * _SHORT_SIDE_PAD) / height
439
+ expected_bounds = [x0, y0, dx, dy]
440
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
441
+ elif cbar_location == "left":
442
+ for cax, (row, col) in zip(caxes, indexes):
443
+ cax_bounds = get_bounds(fig, cax)
444
+ x0 = (_LEFT_PAD + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)) / width
445
+ y0 = (
446
+ _BOTTOM_PAD
447
+ + row * (_VERTICAL_INTERNAL_PAD + tile_height)
448
+ + _SHORT_SIDE_PAD
449
+ ) / height
450
+ dx = _CBAR_THICKNESS / width
451
+ dy = (tile_height - 2.0 * _SHORT_SIDE_PAD) / height
452
+ expected_bounds = [x0, y0, dx, dy]
453
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
454
+
455
+
456
+ def check_constrained_axes_positions_edge(grid):
457
+ # The positions of the axes are the same as for cbar_mode='single'
458
+ check_constrained_axes_positions_single(grid)
459
+
460
+
461
+ def check_constrained_caxes_positions_edge(grid):
462
+ rows, cols = grid.rows, grid.cols
463
+ width, height = grid.width, grid.height
464
+ tile_width, tile_height = get_tile_width(grid), get_tile_height(grid)
465
+ cbar_location = grid.cbar_location
466
+ fig = grid.fig
467
+
468
+ caxes = grid.caxes
469
+ if cbar_location == "bottom":
470
+ for col, cax in zip(range(cols), caxes):
471
+ cax_bounds = get_bounds(fig, cax)
472
+ x0 = (
473
+ _LEFT_PAD
474
+ + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)
475
+ + _SHORT_SIDE_PAD
476
+ ) / width
477
+ y0 = _BOTTOM_PAD / height
478
+ dx = (tile_width - 2.0 * _SHORT_SIDE_PAD) / width
479
+ dy = _CBAR_THICKNESS / height
480
+ expected_bounds = [x0, y0, dx, dy]
481
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
482
+ elif cbar_location == "top":
483
+ for col, cax in zip(range(cols), caxes):
484
+ cax_bounds = get_bounds(fig, cax)
485
+ x0 = (
486
+ _LEFT_PAD
487
+ + col * (_HORIZONTAL_INTERNAL_PAD + tile_width)
488
+ + _SHORT_SIDE_PAD
489
+ ) / width
490
+ y0 = (height - _CBAR_THICKNESS - _TOP_PAD) / height
491
+ dx = (tile_width - 2.0 * _SHORT_SIDE_PAD) / width
492
+ dy = _CBAR_THICKNESS / height
493
+ expected_bounds = [x0, y0, dx, dy]
494
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
495
+ elif cbar_location == "right":
496
+ for row, cax in zip(range(rows - 1, -1, -1), caxes):
497
+ cax_bounds = get_bounds(fig, cax)
498
+ x0 = (width - _CBAR_THICKNESS - _RIGHT_PAD) / width
499
+ y0 = (
500
+ _BOTTOM_PAD
501
+ + row * (_VERTICAL_INTERNAL_PAD + tile_height)
502
+ + _SHORT_SIDE_PAD
503
+ ) / height
504
+ dx = _CBAR_THICKNESS / width
505
+ dy = (tile_height - 2.0 * _SHORT_SIDE_PAD) / height
506
+ expected_bounds = [x0, y0, dx, dy]
507
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
508
+ elif cbar_location == "left":
509
+ for row, cax in zip(range(rows - 1, -1, -1), caxes):
510
+ cax_bounds = get_bounds(fig, cax)
511
+ x0 = _LEFT_PAD / width
512
+ y0 = (
513
+ _BOTTOM_PAD
514
+ + row * (_VERTICAL_INTERNAL_PAD + tile_height)
515
+ + _SHORT_SIDE_PAD
516
+ ) / height
517
+ dx = _CBAR_THICKNESS / width
518
+ dy = (tile_height - 2.0 * _SHORT_SIDE_PAD) / height
519
+ expected_bounds = [x0, y0, dx, dy]
520
+ np.testing.assert_allclose(cax_bounds, expected_bounds)
521
+
522
+
523
+ def shared_grid(sharex, sharey):
524
+ return WidthConstrainedAxesGrid(
525
+ 2,
526
+ 2,
527
+ width=_WIDTH_CONSTRAINT,
528
+ aspect=_ASPECT_CONSTRAINT,
529
+ sharex=sharex,
530
+ sharey=sharey,
531
+ cbar_mode="single",
532
+ )
533
+
534
+
535
+ def assert_visible_xticklabels(ax):
536
+ assert ax.xaxis._get_tick(ax.xaxis.major).label1.get_visible()
537
+ assert ax.xaxis._get_tick(ax.xaxis.minor).label1.get_visible()
538
+
539
+
540
+ def assert_invisible_xticklabels(ax):
541
+ assert not ax.xaxis._get_tick(ax.xaxis.major).label1.get_visible()
542
+ assert not ax.xaxis._get_tick(ax.xaxis.minor).label1.get_visible()
543
+
544
+
545
+ def assert_visible_yticklabels(ax):
546
+ assert ax.yaxis._get_tick(ax.yaxis.major).label1.get_visible()
547
+ assert ax.yaxis._get_tick(ax.yaxis.minor).label1.get_visible()
548
+
549
+
550
+ def assert_invisible_yticklabels(ax):
551
+ assert not ax.yaxis._get_tick(ax.yaxis.major).label1.get_visible()
552
+ assert not ax.yaxis._get_tick(ax.yaxis.minor).label1.get_visible()
553
+
554
+
555
+ def assert_valid_x_sharing(shared_grid, sharex):
556
+ axes = np.reshape(shared_grid.axes, (shared_grid.rows, shared_grid.cols))
557
+ if sharex in ["all", True]:
558
+ ax_ref = axes.flatten()[0]
559
+ for ax in axes.flatten():
560
+ assert ax.xaxis.major == ax_ref.xaxis.major
561
+ assert ax.xaxis.minor == ax_ref.xaxis.minor
562
+ for ax in axes[:-1, :].flatten():
563
+ assert_invisible_xticklabels(ax)
564
+ for ax in axes[-1, :].flatten():
565
+ assert_visible_xticklabels(ax)
566
+ elif sharex == "row":
567
+ for row in axes:
568
+ ax_ref = row[0]
569
+ for ax in row:
570
+ assert ax.xaxis.major == ax_ref.xaxis.major
571
+ assert ax.xaxis.minor == ax_ref.xaxis.minor
572
+ for col in axes.T:
573
+ ax_ref = col[0]
574
+ for ax in col[1:]:
575
+ assert ax.xaxis.major != ax_ref.xaxis.major
576
+ assert ax.xaxis.minor != ax_ref.xaxis.minor
577
+ for ax in axes.flatten():
578
+ assert_visible_xticklabels(ax)
579
+ elif sharex == "col":
580
+ for col in axes.T:
581
+ ax_ref = col[0]
582
+ for ax in col:
583
+ assert ax.xaxis.major == ax_ref.xaxis.major
584
+ assert ax.xaxis.minor == ax_ref.xaxis.minor
585
+ for row in axes:
586
+ ax_ref = row[0]
587
+ for ax in row[1:]:
588
+ assert ax.xaxis.major != ax_ref.xaxis.major
589
+ assert ax.xaxis.minor != ax_ref.xaxis.minor
590
+ for ax in axes[:-1, :].flatten():
591
+ assert_invisible_xticklabels(ax)
592
+ for ax in axes[-1, :].flatten():
593
+ assert_visible_xticklabels(ax)
594
+ elif sharex in ["none", False]:
595
+ ax_ref = axes.flatten()[0]
596
+ for ax in axes.flatten()[1:]:
597
+ assert ax.xaxis.major != ax_ref.xaxis.major
598
+ assert ax.xaxis.minor != ax_ref.xaxis.minor
599
+ for ax in axes.flatten():
600
+ assert_visible_xticklabels(ax)
601
+
602
+
603
+ def assert_valid_y_sharing(shared_grid, sharey):
604
+ axes = np.reshape(shared_grid.axes, (shared_grid.rows, shared_grid.cols))
605
+ if sharey in ["all", True]:
606
+ ax_ref = axes.flatten()[0]
607
+ for ax in axes.flatten():
608
+ assert ax.yaxis.major == ax_ref.yaxis.major
609
+ assert ax.yaxis.minor == ax_ref.yaxis.minor
610
+ for ax in axes[:, 1:].flatten():
611
+ assert_invisible_yticklabels(ax)
612
+ for ax in axes[:, 0].flatten():
613
+ assert_visible_yticklabels(ax)
614
+ elif sharey == "row":
615
+ for row in axes:
616
+ ax_ref = row[0]
617
+ for ax in row:
618
+ assert ax.yaxis.major == ax_ref.yaxis.major
619
+ assert ax.yaxis.minor == ax_ref.yaxis.minor
620
+ for col in axes.T:
621
+ ax_ref = col[0]
622
+ for ax in col[1:]:
623
+ assert ax.yaxis.major != ax_ref.yaxis.major
624
+ assert ax.yaxis.minor != ax_ref.yaxis.minor
625
+ for ax in axes[:, 1:].flatten():
626
+ assert_invisible_yticklabels(ax)
627
+ for ax in axes[:, 0].flatten():
628
+ assert_visible_yticklabels(ax)
629
+ elif sharey == "col":
630
+ for col in axes.T:
631
+ ax_ref = col[0]
632
+ for ax in col:
633
+ assert ax.yaxis.major == ax_ref.yaxis.major
634
+ assert ax.yaxis.minor == ax_ref.yaxis.minor
635
+ for row in axes:
636
+ ax_ref = row[0]
637
+ for ax in row[1:]:
638
+ assert ax.yaxis.major != ax_ref.yaxis.major
639
+ assert ax.yaxis.minor != ax_ref.yaxis.minor
640
+ for ax in axes.flatten():
641
+ assert_visible_yticklabels(ax)
642
+ elif sharey in ["none", False]:
643
+ ax_ref = axes.flatten()[0]
644
+ for ax in axes.flatten()[1:]:
645
+ assert ax.yaxis.major != ax_ref.yaxis.major
646
+ assert ax.yaxis.minor != ax_ref.yaxis.minor
647
+ for ax in axes.flatten():
648
+ assert_visible_yticklabels(ax)
649
+
650
+
651
+ _SHARE_OPTIONS = ["all", "row", "col", "none", True, False]
652
+
653
+
654
+ @pytest.mark.parametrize(("sharex", "sharey"), product(_SHARE_OPTIONS, _SHARE_OPTIONS))
655
+ def test_share_axes_mixin(sharex, sharey):
656
+ grid = shared_grid(sharex, sharey)
657
+ assert_valid_x_sharing(grid, sharex)
658
+ assert_valid_y_sharing(grid, sharey)
659
+ plt.close(grid.fig)
660
+
661
+
662
+ def test_cartopy():
663
+ pytest.importorskip("cartopy")
664
+ import cartopy.crs as ccrs
665
+ from cartopy.mpl.geoaxes import GeoAxes
666
+
667
+ fig, axes = faceted(
668
+ 2,
669
+ 2,
670
+ width=_WIDTH_CONSTRAINT,
671
+ aspect=_ASPECT_CONSTRAINT,
672
+ axes_kwargs={"projection": ccrs.PlateCarree()},
673
+ )
674
+ for ax in axes:
675
+ assert isinstance(ax, GeoAxes)
676
+ plt.close(fig)
677
+
678
+
679
+ @pytest.mark.parametrize(
680
+ ("cbar_mode", "cbar_expected"),
681
+ [(None, False), ("single", True), ("edge", True), ("each", True)],
682
+ )
683
+ def test_faceted_ax(cbar_mode, cbar_expected):
684
+ if cbar_expected:
685
+ fig, ax, cax = faceted_ax(
686
+ cbar_mode=cbar_mode, width=_WIDTH_CONSTRAINT, aspect=_ASPECT_CONSTRAINT
687
+ )
688
+ assert isinstance(fig, matplotlib.figure.Figure)
689
+ assert isinstance(ax, matplotlib.axes.Axes)
690
+ assert isinstance(cax, matplotlib.axes.Axes)
691
+ else:
692
+ fig, ax = faceted_ax(
693
+ cbar_mode=cbar_mode, width=_WIDTH_CONSTRAINT, aspect=_ASPECT_CONSTRAINT
694
+ )
695
+ assert isinstance(fig, matplotlib.figure.Figure)
696
+ assert isinstance(ax, matplotlib.axes.Axes)