Skip to content

Commit

Permalink
Merge pull request #188 from raphaelquast/dev
Browse files Browse the repository at this point in the history
Merge for v7.2.1
  • Loading branch information
raphaelquast authored Oct 9, 2023
2 parents bfacd50 + fb4ba84 commit d44423f
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 104 deletions.
1 change: 1 addition & 0 deletions docs/api_callbacks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ In addition, each callback-container supports the following useful methods:
share_events
forward_events
add_temporary_artist
set_execute_during_toolbar_action


Using callbacks with the companion-widget
Expand Down
18 changes: 13 additions & 5 deletions eomaps/_data_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ def on_fetch_bg(self, layer=None, bbox=None, check_redraw=True):
return False

props = self.get_props()
if props is None:
if props is None or props["x0"] is None or props["y0"] is None:
# fail-fast in case the data is completely outside the extent
return

Expand Down Expand Up @@ -524,9 +524,10 @@ def on_fetch_bg(self, layer=None, bbox=None, check_redraw=True):
self.m.cb.pick._set_artist(coll)

except Exception as ex:
raise AssertionError(
f"EOmaps: Unable to plot the data for the layer '{layer}'!"
) from ex
_log.exception(
f"EOmaps: Unable to plot the data for the layer '{layer}'!",
exc_info=_log.getEffectiveLevel() <= logging.DEBUG,
)

def data_in_extent(self, extent):
# check if the data extent collides with the map extent
Expand Down Expand Up @@ -773,7 +774,9 @@ def _zoom(self, blocksize):
valid_fraction = getattr(self.m.shape, "_valid_fraction", 0)

# only zoom if the shape provides a _maxsize attribute
if maxsize is None or self._current_data["z_data"].size < maxsize:
if self._current_data["z_data"] is None or maxsize is None:
return
elif self._current_data["z_data"].size < maxsize:
return

if method == "spline":
Expand Down Expand Up @@ -840,6 +843,11 @@ def _zoom_block(self, maxsize, method, valid_fraction, blocksize):
self._current_data["z_data"] = blocks.sum(axis=(-1, -2))
elif method == "median":
self._current_data["z_data"] = np.median(blocks, axis=(-1, -2))
elif method == "mode":
from scipy.stats import mode

out, counts = mode(blocks, axis=(-1, -2), nan_policy="propagate")
self._current_data["z_data"] = out
elif method == "fast_sum":
self._current_data["z_data"] = self._fast_block_metric(blocks, bs, False)
elif method == "fast_mean":
Expand Down
2 changes: 1 addition & 1 deletion eomaps/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "7.2"
__version__ = "7.2.1"
14 changes: 14 additions & 0 deletions eomaps/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@ def annotate(
)

styledict.update(**kwargs)
# use a black font-color by default to avoid issues if rcparams are
# set differently
styledict.setdefault("color", "k")
annotation = self.m.ax.annotate("", xy=picked_pos, **styledict)
annotation.set_zorder(zorder)

Expand Down Expand Up @@ -1208,9 +1211,20 @@ class MoveCallbacks(_ClickCallbacks):
"peek_layer",
]

def _decorate(self, f):
def inner(*args, **kwargs):
f(*args, **kwargs)
self.m.BM.update()

return inner

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

for cb in self._cb_list:
if cb not in ["print_to_console"]:
setattr(self, cb, self._decorate(getattr(super(), cb)))


class KeypressCallbacks:
"""Collection of callbacks that are executed if you press a key on the keyboard."""
Expand Down
63 changes: 44 additions & 19 deletions eomaps/cb_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def __init__(self, m, cb_class=None, method="click", parent_container=None):
self._event = None

self._execute_on_all_layers = False
self._execute_while_toolbar_active = False

def _getobj(self, m):
"""Get the equivalent callback container on another maps object."""
Expand Down Expand Up @@ -328,6 +329,35 @@ def set_execute_on_all_layers(self, q):
)
self._execute_on_all_layers = q

def _check_toolbar_mode(self):
if self._execute_while_toolbar_active:
return False

# returns True if a toolbar mode is active and False otherwise
if (
self._m.f.canvas.toolbar is not None
) and self._m.f.canvas.toolbar.mode != "":
return True
else:
return False

def set_execute_during_toolbar_action(self, q):
"""
Set if callbacks should be executed during a toolbar action (e.g. pan/zoom).
By default, callbacks are not executed during toolbar actions to make sure
pan/zoom is smooth. (e.g. to avoid things like constant re-fetching of webmaps
if a peek-layer callback is active during pan/zoom)
Parameters
----------
q : bool
If True, callbacks will be triggered independent of the toolbar state.
if False, callbacks will only trigger if no toolbar action is active.
"""
self._execute_while_toolbar_active = q


class _ClickContainer(_CallbackContainer):
"""
Expand Down Expand Up @@ -993,9 +1023,7 @@ def clickcb(event):
self._event = event

# don't execute callbacks if a toolbar-action is active
if (
self._m.f.canvas.toolbar is not None
) and self._m.f.canvas.toolbar.mode != "":
if self._check_toolbar_mode():
return

# execute onclick on the maps object that belongs to the clicked axis
Expand Down Expand Up @@ -1023,9 +1051,7 @@ def releasecb(event):
self._event = event

# don't execute callbacks if a toolbar-action is active
if (
self._m.f.canvas.toolbar is not None
) and self._m.f.canvas.toolbar.mode != "":
if self._check_toolbar_mode():
return

# execute onclick on the maps object that belongs to the clicked axis
Expand Down Expand Up @@ -1121,13 +1147,16 @@ class MoveContainer(ClickContainer):

# this is just a copy of ClickContainer to manage motion-sensitive callbacks

def __init__(self, button_down=False, *args, **kwargs):
def __init__(self, button_down=False, update_on_trigger=True, *args, **kwargs):

super().__init__(*args, **kwargs)

self._cid_motion_event = None

self._button_down = button_down

self._update_on_trigger = update_on_trigger

def _init_cbs(self):
if self._m.parent is self._m:
self._add_move_callback()
Expand All @@ -1148,7 +1177,7 @@ def movecb(event):

try:
self._event = event
# only execute movecb if a mouse-button is holded down
# only execute movecb if a mouse-button is held down
# and only if the motion is happening inside the axes
if self._button_down:
if not event.button: # or (event.inaxes != self._m.ax):
Expand All @@ -1168,9 +1197,7 @@ def movecb(event):
return

# don't execute callbacks if a toolbar-action is active
if (
self._m.f.canvas.toolbar is not None
) and self._m.f.canvas.toolbar.mode != "":
if self._check_toolbar_mode():
return

# execute onclick on the maps object that belongs to the clicked axis
Expand All @@ -1192,8 +1219,8 @@ def movecb(event):
obj._fwd_cb(event)

# only update if a callback is attached
# (to avoid constantly calling update)
if update:
# (to avoid lag in webagg backed due to slow updates)
if self._update_on_trigger and update:
if self._button_down:
if event.button:
self._m.parent.BM.update(clear=self._method)
Expand Down Expand Up @@ -1470,9 +1497,7 @@ def _onpick(self, event):
return

# don't execute callbacks if a toolbar-action is active
if (
self._m.f.canvas.toolbar is not None
) and self._m.f.canvas.toolbar.mode != "":
if self._check_toolbar_mode():
return

# make sure temporary artists are cleared before executing new callbacks
Expand Down Expand Up @@ -1550,9 +1575,7 @@ def pickcb(event):
return

# don't execute callbacks if a toolbar-action is active
if (
self._m.f.canvas.toolbar is not None
) and self._m.f.canvas.toolbar.mode != "":
if self._check_toolbar_mode():
return

if not self._artist_picked(event):
Expand Down Expand Up @@ -1963,6 +1986,7 @@ def __init__(self, m):
method="_click_move",
parent_container=self._click,
button_down=True,
update_on_trigger=True, # automatically trigger updates for click+move!
)

self._move = MoveContainer(
Expand All @@ -1971,6 +1995,7 @@ def __init__(self, m):
method="move",
button_down=False,
default_button=None,
update_on_trigger=False, # dont trigger updates for move!
)

self._pick = PickContainer(
Expand Down
45 changes: 33 additions & 12 deletions eomaps/eomaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -1958,29 +1958,24 @@ def add_gdf(
- if "gpd": re-project geometries geopandas
- if "cartopy": re-project geometries with cartopy (slower but more robust)
The default is "gpd".
>>> mg = MapsGrid(2, 1, crs=Maps.CRS.Stereographic())
>>> mg.m_0_0.add_feature.preset.ocean(reproject="gpd")
>>> mg.m_1_0.add_feature.preset.ocean(reproject="cartopy")
The default is "gpd".
verbose : bool, optional
Indicator if a progressbar should be printed when re-projecting
geometries with "use_gpd=False".
The default is False.
geometries with "use_gpd=False". The default is False.
only_valid : bool, optional
- If True, only valid geometries (e.g. `gdf.is_valid`) are plotted.
- If False, all geometries are attempted to be plotted
(this might result in errors for infinite geometries etc.)
The default is True
set_extent: bool, optional
- if True, set the map extent to the extent of the geometries with
a +-5% margin.
- if float, use the value se margin.
- if True, set map extent to the extent of the geometries with +-5% margin.
- if float, use the value as margin (0-1).
The default is True.
kwargs :
Expand Down Expand Up @@ -3484,6 +3479,12 @@ def text(self, *args, layer=None, **kwargs):
@wraps(plt.savefig)
def savefig(self, *args, refetch_wms=False, rasterize_data=True, **kwargs):
"""Save the figure."""
if plt.get_backend() == "agg":
# make sure that a draw-event was triggered when using the agg backend
# (to avoid export-issues with some shapes)
# TODO properly assess why this is necessary!
self.f.canvas.draw_idle()

with ExitStack() as stack:
if refetch_wms is False:
if _cx_refetch_wms_on_size_change is not None:
Expand Down Expand Up @@ -3602,6 +3603,9 @@ def savefig(self, *args, refetch_wms=False, rasterize_data=True, **kwargs):
# trigger a redraw of all savelayers to make sure unmanaged artists
# and ordinary matplotlib axes are properly drawn
self.redraw(*savelayers)
# flush events prior to savefig to avoi dissues with pending draw events
# that cause wrong positioning of grid-labels and missing artists!
self.f.canvas.flush_events()
self.f.savefig(*args, **kwargs)

if redraw is True:
Expand Down Expand Up @@ -4007,11 +4011,28 @@ def _save_to_clipboard(self, **kwargs):
cb.setImage(QImage.fromData(buffer.getvalue()))

def _on_keypress(self, event):
if plt.get_backend().lower() == "webagg":
return

# NOTE: callback is only attached to the parent Maps object!
if event.key == self._companion_widget_key:
self._open_companion_widget((event.x, event.y))
try:
self._open_companion_widget((event.x, event.y))
except Exception:
_log.exception(
"EOmaps: Encountered a problem while trying to open "
"the companion widget",
exec_info=_log.getEffectiveLevel() <= logging.DEBUG,
)
elif event.key == "ctrl+c":
self._save_to_clipboard(**Maps._clipboard_kwargs)
try:
self._save_to_clipboard(**Maps._clipboard_kwargs)
except Exception:
_log.exception(
"EOmaps: Encountered a problem while trying to export the figure "
"to the clipboard.",
exec_info=_log.getEffectiveLevel() <= logging.DEBUG,
)

def _init_figure(self, ax=None, plot_crs=None, **kwargs):
if self.parent.f is None:
Expand Down
Loading

0 comments on commit d44423f

Please sign in to comment.