|
479 | 479 | # standalone semantic keys (categories, size scales, color levels, or geometry types). |
480 | 480 | # UltraPlot provides helper methods that build these entries directly: |
481 | 481 | # |
| 482 | +# * :meth:`~ultraplot.axes.Axes.entrylegend` |
482 | 483 | # * :meth:`~ultraplot.axes.Axes.catlegend` |
483 | 484 | # * :meth:`~ultraplot.axes.Axes.sizelegend` |
484 | 485 | # * :meth:`~ultraplot.axes.Axes.numlegend` |
485 | 486 | # * :meth:`~ultraplot.axes.Axes.geolegend` |
| 487 | +# |
| 488 | +# These helpers are useful whenever the legend should describe an encoding rather than |
| 489 | +# mirror artists that already happen to be drawn. In practice there are two distinct |
| 490 | +# workflows: |
| 491 | +# |
| 492 | +# * Use :meth:`~ultraplot.axes.Axes.legend` when you already have artists and want to |
| 493 | +# reuse their labels or lightly restyle the legend handles. |
| 494 | +# * Use the semantic helpers when you want to define the legend from meaning-first |
| 495 | +# inputs such as categories, numeric size levels, numeric color levels, or geometry |
| 496 | +# types, even if no matching exemplar artist exists on the axes. |
| 497 | +# |
| 498 | +# Choosing a helper |
| 499 | +# ~~~~~~~~~~~~~~~~~ |
| 500 | +# |
| 501 | +# * :meth:`~ultraplot.axes.Axes.entrylegend` is the most general helper. Use it when |
| 502 | +# you want explicit labels, mixed line and marker entries, or fully custom legend |
| 503 | +# rows that are not easily described by a single category or numeric scale. |
| 504 | +# * :meth:`~ultraplot.axes.Axes.catlegend` is for discrete categories mapped to colors, |
| 505 | +# markers, and optional line styles. Labels come from the category names. |
| 506 | +# * :meth:`~ultraplot.axes.Axes.sizelegend` is for marker-size semantics. Labels are |
| 507 | +# derived from the numeric levels by default, can be formatted with ``fmt=``, and |
| 508 | +# can now be overridden directly with ``labels=[...]`` or ``labels={level: label}``. |
| 509 | +# * :meth:`~ultraplot.axes.Axes.numlegend` is for numeric color encodings rendered as |
| 510 | +# discrete patches without requiring a pre-existing mappable. |
| 511 | +# * :meth:`~ultraplot.axes.Axes.geolegend` is for shapes and map-like semantics. It can |
| 512 | +# mix named symbols, Shapely geometries, and country shorthands in one legend. |
| 513 | +# |
| 514 | +# The helpers are intentionally composable. Each one accepts ``add=False`` and returns |
| 515 | +# ``(handles, labels)`` so you can merge semantic sections and pass the result through |
| 516 | +# :meth:`~ultraplot.axes.Axes.legend` yourself. |
| 517 | +# |
| 518 | +# .. code-block:: python |
| 519 | +# |
| 520 | +# # Reuse plotted artists when they already exist. |
| 521 | +# hs = ax.plot(data, labels=["control", "treatment"]) |
| 522 | +# ax.legend(hs, loc="r") |
| 523 | +# |
| 524 | +# # Build a category key without plotting one exemplar artist per category. |
| 525 | +# ax.catlegend( |
| 526 | +# ["Control", "Treatment"], |
| 527 | +# colors={"Control": "blue7", "Treatment": "red7"}, |
| 528 | +# markers={"Control": "o", "Treatment": "^"}, |
| 529 | +# loc="r", |
| 530 | +# ) |
| 531 | +# |
| 532 | +# # Build fully custom entries with explicit labels and mixed semantics. |
| 533 | +# ax.entrylegend( |
| 534 | +# [ |
| 535 | +# { |
| 536 | +# "label": "Observed samples", |
| 537 | +# "line": False, |
| 538 | +# "marker": "o", |
| 539 | +# "markersize": 8, |
| 540 | +# "markerfacecolor": "blue7", |
| 541 | +# "markeredgecolor": "black", |
| 542 | +# }, |
| 543 | +# { |
| 544 | +# "label": "Model fit", |
| 545 | +# "line": True, |
| 546 | +# "color": "black", |
| 547 | +# "linewidth": 2.5, |
| 548 | +# "linestyle": "--", |
| 549 | +# }, |
| 550 | +# ], |
| 551 | +# title="Entry styles", |
| 552 | +# loc="l", |
| 553 | +# ) |
| 554 | +# |
| 555 | +# # Size legends can format labels automatically or accept explicit labels. |
| 556 | +# ax.sizelegend( |
| 557 | +# [10, 50, 200], |
| 558 | +# labels=["Small", "Medium", "Large"], |
| 559 | +# title="Population", |
| 560 | +# loc="ur", |
| 561 | +# ) |
| 562 | +# |
| 563 | +# # Numeric color legends are discrete color keys decoupled from a mappable. |
| 564 | +# ax.numlegend(vmin=0, vmax=1, n=5, cmap="viko", fmt="{:.2f}", loc="ll") |
| 565 | +# |
| 566 | +# # Geometry legends can mix named shapes, Shapely geometries, and country codes. |
| 567 | +# ax.geolegend([("Triangle", "triangle"), ("Australia", "country:AU")], loc="r") |
| 568 | +# |
| 569 | +# .. code-block:: python |
| 570 | +# |
| 571 | +# # Compose multiple semantic helpers into one legend. |
| 572 | +# size_handles, size_labels = ax.sizelegend( |
| 573 | +# [10, 50, 200], |
| 574 | +# labels=["Small", "Medium", "Large"], |
| 575 | +# add=False, |
| 576 | +# ) |
| 577 | +# entry_handles, entry_labels = ax.entrylegend( |
| 578 | +# [ |
| 579 | +# { |
| 580 | +# "label": "Observed", |
| 581 | +# "line": False, |
| 582 | +# "marker": "o", |
| 583 | +# "markerfacecolor": "blue7", |
| 584 | +# }, |
| 585 | +# { |
| 586 | +# "label": "Fit", |
| 587 | +# "line": True, |
| 588 | +# "color": "black", |
| 589 | +# "linewidth": 2, |
| 590 | +# }, |
| 591 | +# ], |
| 592 | +# add=False, |
| 593 | +# ) |
| 594 | +# ax.legend( |
| 595 | +# size_handles + entry_handles, |
| 596 | +# size_labels + entry_labels, |
| 597 | +# loc="r", |
| 598 | +# title="Combined semantic key", |
| 599 | +# ) |
486 | 600 |
|
487 | 601 | # %% |
488 | 602 | import cartopy.crs as ccrs |
489 | 603 | import shapely.geometry as sg |
490 | 604 |
|
491 | | -fig, ax = uplt.subplots(refwidth=4.2) |
| 605 | +fig, ax = uplt.subplots(refwidth=5.0) |
492 | 606 | ax.format(title="Semantic legend helpers", grid=False) |
493 | 607 |
|
| 608 | +ax.entrylegend( |
| 609 | + [ |
| 610 | + { |
| 611 | + "label": "Observed samples", |
| 612 | + "line": False, |
| 613 | + "marker": "o", |
| 614 | + "markersize": 8, |
| 615 | + "markerfacecolor": "blue7", |
| 616 | + "markeredgecolor": "black", |
| 617 | + }, |
| 618 | + { |
| 619 | + "label": "Model fit", |
| 620 | + "line": True, |
| 621 | + "color": "black", |
| 622 | + "linewidth": 2.5, |
| 623 | + "linestyle": "--", |
| 624 | + }, |
| 625 | + ], |
| 626 | + loc="l", |
| 627 | + title="Entry styles", |
| 628 | + frameon=False, |
| 629 | +) |
494 | 630 | ax.catlegend( |
495 | 631 | ["A", "B", "C"], |
496 | 632 | colors={"A": "red7", "B": "green7", "C": "blue7"}, |
|
500 | 636 | ) |
501 | 637 | ax.sizelegend( |
502 | 638 | [10, 50, 200], |
| 639 | + labels=["Small", "Medium", "Large"], |
503 | 640 | loc="upper right", |
504 | 641 | title="Population", |
505 | 642 | ncols=1, |
|
0 commit comments