Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
732b004
restore sharing behavior
cvanelteren Sep 16, 2025
fbd946a
add unittest submodule intended for testing sharing related functions
cvanelteren Sep 16, 2025
923f509
rm auto add import
cvanelteren Sep 16, 2025
639695d
restore figure share
cvanelteren Sep 16, 2025
6b1dffe
rm _get_sharing_level
cvanelteren Sep 16, 2025
b3a37ae
update figure format
cvanelteren Sep 16, 2025
724d0da
bump handle axis sharing geo
cvanelteren Sep 16, 2025
60a2848
bump handle axis sharing geo
cvanelteren Sep 16, 2025
010001e
refactor and update test
cvanelteren Sep 16, 2025
d63d933
change x test to be mpl specific
cvanelteren Sep 16, 2025
452533e
change x test to be mpl specific
cvanelteren Sep 16, 2025
7394633
revert check
cvanelteren Sep 16, 2025
35e6408
Merge branch 'main' into fix-sharing
cvanelteren Sep 16, 2025
cdc220c
reversion to new
cvanelteren Sep 16, 2025
10e329a
formatting
cvanelteren Sep 16, 2025
13702c0
rm added import
cvanelteren Sep 16, 2025
08e77e3
fix test
cvanelteren Sep 16, 2025
f962859
minor formatting
cvanelteren Sep 16, 2025
3aea236
satisfy codecov
cvanelteren Sep 16, 2025
09b0d67
Merge branch 'main' into fix-sharing
cvanelteren Sep 19, 2025
ddb1ef7
Update ultraplot/tests/test_sharing.py
cvanelteren Sep 19, 2025
6341074
Update ultraplot/tests/test_sharing.py
cvanelteren Sep 19, 2025
345009e
Update ultraplot/tests/test_sharing.py
cvanelteren Sep 19, 2025
d65c141
restore typo with sharing level
cvanelteren Sep 19, 2025
b1a8b06
clarify comment
cvanelteren Sep 19, 2025
f720d09
specify sharing limit
cvanelteren Sep 19, 2025
579cadd
update geosharing
cvanelteren Sep 21, 2025
e18b02a
propagate shared axis for border without shared axis
cvanelteren Sep 21, 2025
c52f0e6
move apply axis to each subtype
cvanelteren Sep 22, 2025
e31cf67
add import on polar
cvanelteren Sep 22, 2025
1c62fef
remove redundant function
cvanelteren Sep 22, 2025
f76e4af
refactor cartesian sharing
cvanelteren Sep 22, 2025
c656a17
fixed sharing edge cases cartesian
cvanelteren Sep 23, 2025
e2c2989
refactor sharing handler to be similar to cartesian
cvanelteren Sep 23, 2025
4ea7867
further fixes for cartesian sharing
cvanelteren Sep 23, 2025
7fa2120
update gridliner sharing
cvanelteren Sep 23, 2025
57a5f33
update tests
cvanelteren Sep 23, 2025
7e3b5a1
update tests part 2
cvanelteren Sep 23, 2025
b4e8bcf
remove redundant draw
cvanelteren Sep 23, 2025
05515a3
remove redundant tests
cvanelteren Sep 23, 2025
20e292c
forgot this
cvanelteren Sep 23, 2025
211a100
add override import
cvanelteren Sep 23, 2025
5eb11e2
Move label sharing to axis object
cvanelteren Sep 25, 2025
cf5ba69
Add geo back in
cvanelteren Sep 29, 2025
b2c9fe1
Fix order of label transfer (#353)
cvanelteren Sep 29, 2025
876d1ec
Bump the github-actions group with 2 updates (#354)
dependabot[bot] Oct 1, 2025
39be7e8
revert check
cvanelteren Sep 16, 2025
4610d84
stash
cvanelteren Sep 29, 2025
685c2f7
refactor geo toggling
cvanelteren Oct 1, 2025
7ea819c
more refactoring
cvanelteren Oct 2, 2025
267bcdc
update test to reflect new sharing changes
cvanelteren Oct 3, 2025
ab23182
simplify internal border detection
cvanelteren Oct 3, 2025
21f124d
minor refactor
cvanelteren Oct 3, 2025
53c3b80
more refactoring
cvanelteren Oct 3, 2025
56dfb95
add get for border back
cvanelteren Oct 3, 2025
582c52f
remote debug
cvanelteren Oct 3, 2025
a29061f
make mpl 3.9 compat
cvanelteren Oct 3, 2025
c520e0f
rm debug
cvanelteren Oct 3, 2025
2ba95c3
don't use hidden axes
cvanelteren Oct 3, 2025
fdce0fd
don't use hidden axes
cvanelteren Oct 3, 2025
e82d675
don't use hidden axes
cvanelteren Oct 3, 2025
89fabd9
Merge branch 'main' into fix-sharing
cvanelteren Oct 3, 2025
5f145c0
rm dead code
cvanelteren Oct 3, 2025
92af811
debug autodiverging -- locally passing
cvanelteren Oct 3, 2025
35f5bc1
fix grid indexing
cvanelteren Oct 4, 2025
4deb49c
fix grid indexing
cvanelteren Oct 4, 2025
cd55ccc
add unittest
cvanelteren Oct 4, 2025
7555bb3
Update ultraplot/tests/test_geographic.py
cvanelteren Oct 4, 2025
6dda980
update tests to reflect changes
cvanelteren Oct 4, 2025
1a72ba1
fix indexing
cvanelteren Oct 4, 2025
c94a3c5
add unittest that made docs fail
cvanelteren Oct 4, 2025
5fb9cd9
restore indexing
cvanelteren Oct 4, 2025
98555a8
fix indexing
cvanelteren Oct 4, 2025
711ca15
rm dead code
cvanelteren Oct 4, 2025
dd6ff2a
handle index error
cvanelteren Oct 4, 2025
73f6324
Merge branch 'hotfix-grid-index' into fix-sharing
cvanelteren Oct 4, 2025
a138bba
adjust default panel ticks
cvanelteren Oct 4, 2025
1a97333
Merge branch 'main' into fix-sharing
cvanelteren Oct 9, 2025
212cd06
Merge branch 'main' into fix-sharing
cvanelteren Oct 9, 2025
be2f46b
bump
cvanelteren Oct 9, 2025
a5a4145
bump test
cvanelteren Oct 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ sources
# Python extras
.ipynb_checkpoints
*.log
*.ipnyb
*.pyc
.*.pyc
__pycache__
Expand Down
111 changes: 6 additions & 105 deletions ultraplot/axes/cartesian.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@ def _apply_axis_sharing(self):
# bottommost or to the *right* of the leftmost panel. But the sharing level
# used for the leftmost and bottommost is the *figure* sharing level.

# Get border axes once for efficiency
border_axes = self.figure._get_border_axes()

# Apply X axis sharing
self._apply_axis_sharing_for_axis("x", border_axes)

Expand All @@ -412,128 +410,31 @@ def _apply_axis_sharing_for_axis(
"""
if axis_name == "x":
axis = self.xaxis
shared_axis = self._sharex
panel_group = self._panel_sharex_group
shared_axis = self._sharex # do we share the xaxis?
panel_group = self._panel_sharex_group # do we have a panel?
sharing_level = self.figure._sharex
label_params = ["labeltop", "labelbottom"]
border_sides = ["top", "bottom"]
else: # axis_name == 'y'
axis = self.yaxis
shared_axis = self._sharey
panel_group = self._panel_sharey_group
sharing_level = self.figure._sharey
label_params = ["labelleft", "labelright"]
border_sides = ["left", "right"]

if shared_axis is None or not axis.get_visible():
if not axis.get_visible():
return

level = 3 if panel_group else sharing_level

# Handle axis label sharing (level > 0)
if level > 0:
# If we are a border axis, @shared_axis may be None
# We propagate this through the _determine_tick_label_visiblity() logic
if level > 0 and shared_axis:
shared_axis_obj = getattr(shared_axis, f"{axis_name}axis")
labels._transfer_label(axis.label, shared_axis_obj.label)
axis.label.set_visible(False)

# Handle tick label sharing (level > 2)
if level > 2:
label_visibility = self._determine_tick_label_visibility(
axis,
shared_axis,
axis_name,
label_params,
border_sides,
border_axes,
)
axis.set_tick_params(which="both", **label_visibility)
# Turn minor ticks off
axis.set_minor_formatter(mticker.NullFormatter())

def _determine_tick_label_visibility(
self,
axis: maxis.Axis,
shared_axis: maxis.Axis,
axis_name: str,
label_params: list[str],
border_sides: list[str],
border_axes: dict[str, list[plot.PlotAxes]],
) -> dict[str, bool]:
"""
Determine which tick labels should be visible based on sharing rules and borders.

Parameters
----------
axis : matplotlib axis
The current axis object
shared_axis : Axes
The axes this one shares with
axis_name : str
Either 'x' or 'y'
label_params : list
List of label parameter names (e.g., ['labeltop', 'labelbottom'])
border_sides : list
List of border side names (e.g., ['top', 'bottom'])
border_axes : dict
Dictionary from _get_border_axes()

Returns
-------
dict
Dictionary of label visibility parameters
"""
ticks = axis.get_tick_params()
shared_axis_obj = getattr(shared_axis, f"{axis_name}axis")
sharing_ticks = shared_axis_obj.get_tick_params()

label_visibility = {}

def _convert_label_param(label_param: str) -> str:
# Deal with logic not being consistent
# in prior mpl versions
if version.parse(str(_version_mpl)) <= version.parse("3.9"):
if label_param == "labeltop" and axis_name == "x":
label_param = "labelright"
elif label_param == "labelbottom" and axis_name == "x":
label_param = "labelleft"
return label_param

for label_param, border_side in zip(label_params, border_sides):
# Check if user has explicitly set label location via format()
label_visibility[label_param] = False
has_panel = False
for panel in self._panel_dict[border_side]:
# Check if the panel is a colorbar
colorbars = [
values
for key, values in self._colorbar_dict.items()
if border_side in key # key is tuple (side, top | center | lower)
]
if not panel in colorbars:
# Skip colorbar as their
# yaxis is not shared
has_panel = True
break
# When we have a panel, let the panel have
# the labels and turn-off for this axis + side.
if has_panel:
continue
is_border = self in border_axes.get(border_side, [])
is_panel = (
self in shared_axis._panel_dict[border_side]
and self == shared_axis._panel_dict[border_side][-1]
)
# Use automatic border detection logic
# if we are a panel we "push" the labels outwards
label_param_trans = _convert_label_param(label_param)
is_this_tick_on = ticks[label_param_trans]
is_parent_tick_on = sharing_ticks[label_param_trans]
if is_panel:
label_visibility[label_param] = is_parent_tick_on
elif is_border:
label_visibility[label_param] = is_this_tick_on
return label_visibility

def _add_alt(self, sx, **kwargs):
"""
Add an alternate axes.
Expand Down
74 changes: 31 additions & 43 deletions ultraplot/axes/geo.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,27 +652,16 @@ def _apply_axis_sharing(self):
or to the *right* of the leftmost panel. But the sharing level used for
the leftmost and bottommost is the *figure* sharing level.
"""
# Handle X axis sharing
if self._sharex:
self._handle_axis_sharing(
source_axis=self._sharex._lonaxis,
target_axis=self._lonaxis,
)

# Handle Y axis sharing
if self._sharey:
self._handle_axis_sharing(
source_axis=self._sharey._lataxis,
target_axis=self._lataxis,
)
# Share interval x
if self._sharex and self.figure._sharex >= 2:
self._lonaxis.set_view_interval(*self._sharex._lonaxis.get_view_interval())
self._lonaxis.set_minor_locator(self._sharex._lonaxis.get_minor_locator())

# This block is apart of the draw sequence as the
# gridliner object is created late in the
# build chain.
if not self.stale:
return
if self.figure._get_sharing_level() == 0:
return
# Share interval y
if self._sharey and self.figure._sharey >= 2:
self._lataxis.set_view_interval(*self._sharey._lataxis.get_view_interval())
self._lataxis.set_minor_locator(self._sharey._lataxis.get_minor_locator())

def _get_gridliner_labels(
self,
Expand All @@ -691,38 +680,36 @@ def _toggle_gridliner_labels(
labelright=None,
geo=None,
):
# For BasemapAxes the gridlines are dicts with key as the coordinate and keys the line and label
# We override the dict here assuming the labels are mut excl due to the N S E W extra chars
"""
Toggle visibility of gridliner labels for each direction.

Parameters
----------
labeltop, labelbottom, labelleft, labelright : bool or None
Whether to show labels on each side. If None, do not change.
geo : optional
Not used in this method.
"""
# Ensure gridlines_major is fully initialized
if any(i is None for i in self.gridlines_major):
return

gridlabels = self._get_gridliner_labels(
bottom=labelbottom, top=labeltop, left=labelleft, right=labelright
)
bools = [labelbottom, labeltop, labelleft, labelright]
directions = "bottom top left right".split()
for direction, toggle in zip(directions, bools):

toggles = {
"bottom": labelbottom,
"top": labeltop,
"left": labelleft,
"right": labelright,
}

for direction, toggle in toggles.items():
if toggle is None:
continue
for label in gridlabels.get(direction, []):
label.set_visible(toggle)

def _handle_axis_sharing(
self,
source_axis: "GeoAxes",
target_axis: "GeoAxes",
):
"""
Helper method to handle axis sharing for both X and Y axes.

Args:
source_axis: The source axis to share from
target_axis: The target axis to apply sharing to
"""
# Copy view interval and minor locator from source to target

if self.figure._get_sharing_level() >= 2:
target_axis.set_view_interval(*source_axis.get_view_interval())
target_axis.set_minor_locator(source_axis.get_minor_locator())
label.set_visible(bool(toggle) or toggle in ("x", "y"))

@override
def draw(self, renderer=None, *args, **kwargs):
Expand Down Expand Up @@ -1441,6 +1428,7 @@ def _is_ticklabel_on(self, side: str) -> bool:
"""
# Deal with different cartopy versions
left_labels, right_labels, bottom_labels, top_labels = self._get_side_labels()

if self.gridlines_major is None:
return False
elif side == "labelleft":
Expand Down
10 changes: 10 additions & 0 deletions ultraplot/axes/polar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"""
import inspect

try:
from typing import override
except:
from typing_extensions import override

import matplotlib.projections.polar as mpolar
import numpy as np

Expand Down Expand Up @@ -138,6 +143,11 @@ def __init__(self, *args, **kwargs):
for axis in (self.xaxis, self.yaxis):
axis.set_tick_params(which="both", size=0)

@override
def _apply_axis_sharing(self):
# Not implemented. Silently pass
return

def _update_formatter(self, x, *, formatter=None, formatter_kw=None):
"""
Update the gridline label formatter.
Expand Down
Loading
Loading