Skip to content

Commit

Permalink
Merge pull request #60 from raphaelquast/dev
Browse files Browse the repository at this point in the history
Merge for v3.3.1
  • Loading branch information
raphaelquast authored Mar 21, 2022
2 parents 6916424 + b67457b commit 0819d7d
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 34 deletions.
59 changes: 53 additions & 6 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,14 @@ is performed (resampling based on the mean-value is used by default).
🌍 Customizing the plot
~~~~~~~~~~~~~~~~~~~~~~~

The general appearance of the plot can be adjusted by setting the ``plot_specs`` and ``classify_specs``
of the Maps object:
Some general specifications for the appearance of the plot can be adjusted by setting the ``plot_specs`` of the Maps object:

.. code-block:: python
m = Maps()
m.set_data(...)
m.set_plot_specs(cmap="RdBu", vmin=0.1, vmax=0.5, histbins=20, alpha=0.75)
m.plot_specs.alpha = 0.5 # alternative way for setting plot-specs
.. currentmodule:: eomaps

Expand All @@ -125,9 +131,20 @@ of the Maps object:
:template: only_names_in_toc.rst

Maps.set_plot_specs
Maps.set_classify_specs
Maps.subplots_adjust

To adjust the margins of the subplots, use ``m.subplots_adjust``, e.g.:

The available classifiers that can be used to classify the data (provided by ``mapclassify``) are accessible via `Maps.CLASSIFIERS`:
.. code-block:: python
m = Maps()
m.subplots_adjust(left=0.1, right=0.9, bottom=0.05, top=0.95)
📊 Data classification
~~~~~~~~~~~~~~~~~~~~~~~

EOmaps provides an interface for `mapclassify <https://github.com/pysal/mapclassify>`_ to classify datasets prior to plotting
via ``m.set_classify_specs``. Available classifiers that can be used are accessible via `Maps.CLASSIFIERS`:

.. code-block:: python
Expand All @@ -138,8 +155,34 @@ The available classifiers that can be used to classify the data (provided by ``m
m.set_classify_specs(Maps.CLASSFIERS.Quantiles, k=5)
m.classify_specs.k = 10 # alternative way for setting classify-specs
m.set_plot_specs(cmap="RdBu", histbins=20)
m.plot_specs.alpha = .5 # alternative way for setting plot-specs
.. currentmodule:: eomaps

.. autosummary::
:toctree: generated
:nosignatures:
:template: only_names_in_toc.rst

Maps.set_classify_specs


Currently available classification-schemes are (see `mapclassify <https://github.com/pysal/mapclassify>`_ for details):

- BoxPlot (hinge)
- EqualInterval (k)
- FisherJenks (k)
- FisherJenksSampled (k, pct, truncate)
- HeadTailBreaks ()
- JenksCaspall (k)
- JenksCaspallForced (k)
- JenksCaspallSampled (k, pct)
- MaxP (k, initial)
- MaximumBreaks (k, mindiff)
- NaturalBreaks (k, initial)
- Quantiles (k)
- Percentiles (pct)
- StdMean (multiples)
- UserDefined (bins)



🗺 Plot the map and save it
Expand All @@ -165,6 +208,7 @@ Some useful arguments that are supported by most shapes (except "shade"-shapes)
...
m.plot_map(fc="none", ec="g", lw=2, alpha=0.5)
You can then continue to add :ref:`colorbar`, :ref:`annotations_and_markers`,
:ref:`scalebar`, :ref:`compass`, :ref:`webmap_layers` or :ref:`geodataframe` to the map,
or you can start to add :ref:`utility` and :ref:`callbacks`.
Expand Down Expand Up @@ -209,6 +253,9 @@ and provides convenience-functions to perform actions on all maps of the figure.
for m in mgrid:
...
# set the margins of the plot-grid
mgrid.subplots_adjust(left=0.1, right=0.9, bottom=0.05, top=0.95, hspace=0.1, wspace=0.05)
Custom grids and mixed axes
***************************

Expand Down
2 changes: 1 addition & 1 deletion eomaps/_webmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ def _image_url(self, tile):

class xyzRasterSource(RasterSource):
"""
A RasterSource that can be used with a SlipperyImageArtist to fetch tiles.
A RasterSource that can be used with a SlippyImageArtist to fetch tiles.
"""

def __init__(self, url, crs, maxzoom=19, transparent=True):
Expand Down
1 change: 0 additions & 1 deletion eomaps/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,6 @@ def highlight_geometry(self, permanent=False, **kwargs):
# get the selected geometry and re-project it to the desired crs
geom = self.m.cb.pick[picker_name].data.loc[[ID]].geometry
# add the geometry to the map

self.m.add_gdf(geom, temporary_picker=picker_name, **kwargs)


Expand Down
59 changes: 41 additions & 18 deletions eomaps/eomaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,11 +346,6 @@ def new_layer(

return m

@property
@lru_cache()
def plot(self):
return plot(self)

@property
@lru_cache()
@wraps(cb_container)
Expand Down Expand Up @@ -1207,13 +1202,17 @@ def _add_colorbar(
tick_precision=3,
density=False,
orientation=None,
log=False,
):

if ax_cb is None:
ax_cb = self.figure.ax_cb
if ax_cb_plot is None:
ax_cb_plot = self.figure.ax_cb_plot

if log:
ax_cb_plot.set_xscale("log")

if z_data is None:
z_data = self._props["z_data"]
z_data = z_data.ravel()
Expand Down Expand Up @@ -1262,7 +1261,6 @@ def _add_colorbar(
spacing="proportional",
orientation=cb_orientation,
)

# plot the histogram
hist_vals, hist_bins, init_hist = ax_cb_plot.hist(
z_data,
Expand Down Expand Up @@ -1361,14 +1359,15 @@ def _add_colorbar(
labelbottom=True,
labeltop=False,
)
ax_cb_plot.xaxis.set_major_locator(plt.MaxNLocator(5))
ax_cb_plot.grid(axis="x", dashes=[5, 5], c="k", alpha=0.5)
# add a line that indicates 0 histogram level
ax_cb_plot.plot(
[1, 1], [0, 1], "k--", alpha=0.5, transform=ax_cb_plot.transAxes
)
# make sure lower x-limit is 0
ax_cb_plot.set_xlim(None, 0)
if log is False:
ax_cb_plot.xaxis.set_major_locator(plt.MaxNLocator(5))
ax_cb_plot.set_xlim(None, 0)

elif orientation == "vertical":
ax_cb_plot.tick_params(
Expand All @@ -1379,14 +1378,15 @@ def _add_colorbar(
labelbottom=False,
labeltop=False,
)
ax_cb_plot.yaxis.set_major_locator(plt.MaxNLocator(5))
ax_cb_plot.grid(axis="y", dashes=[5, 5], c="k", alpha=0.5)
# add a line that indicates 0 histogram level
ax_cb_plot.plot(
[0, 1], [0, 0], "k--", alpha=0.5, transform=ax_cb_plot.transAxes
)
# make sure lower y-limit is 0
ax_cb_plot.set_ylim(0)
if log is False:
ax_cb_plot.yaxis.set_major_locator(plt.MaxNLocator(5))
ax_cb_plot.set_ylim(0)

cb.outline.set_visible(False)

Expand Down Expand Up @@ -2078,6 +2078,7 @@ def add_scalebar(
lon=None,
lat=None,
azim=0,
preset=None,
scale=None,
autoscale_fraction=0.25,
auto_position=(0.75, 0.25),
Expand All @@ -2088,6 +2089,7 @@ def add_scalebar(

s = ScaleBar(
m=self,
preset=preset,
scale=scale,
autoscale_fraction=autoscale_fraction,
auto_position=auto_position,
Expand Down Expand Up @@ -2118,12 +2120,12 @@ def add_wms(self):

@wraps(plt.savefig)
def savefig(self, *args, **kwargs):

# clear all cached background layers before saving to make sure they
# are re-drawn with the correct dpi-settings
self.BM._bg_layers = dict()

self.figure.f.savefig(*args, **kwargs)
# redraw after the save to ensure that backgrounds are correctly cached
self.redraw()

def _shade_map(
self,
Expand Down Expand Up @@ -2500,9 +2502,7 @@ def _plot_map(
ax.set_xlim(max(b.xmin, xmin), min(b.xmax, xmax))
ax.set_ylim(max(b.ymin, ymin), min(b.ymax, ymax))

# self.figure.f.canvas.draw()
if dynamic is True:
self.BM.update(clear=False)
self.figure.f.canvas.draw_idle()

except Exception as ex:
raise ex
Expand Down Expand Up @@ -2595,6 +2595,7 @@ def add_colorbar(
left=0.1,
right=0.05,
layer=None,
log=False,
):
"""
Add a colorbar to an existing figure.
Expand Down Expand Up @@ -2642,6 +2643,9 @@ def add_colorbar(
The padding between the colorbar and the parent axes (as fraction of the
plot-height (if "horizontal") or plot-width (if "vertical")
The default is (0.05, 0.1, 0.1, 0.05)
log : bool, optional
Indicator if the y-axis of the plot should be logarithmic or not.
The default is False
Notes
-----
Expand Down Expand Up @@ -2800,6 +2804,7 @@ def add_colorbar(
density=density,
tick_precision=tick_precision,
histbins=histbins,
log=log,
)

# hide the colorbar if it is not added to the currently visible layer
Expand Down Expand Up @@ -2974,7 +2979,7 @@ def getpos(pos):
figax.set_navigate(False)
figax.set_axis_off()
art = figax.imshow(im, aspect="equal", zorder=999)
self.BM.add_bg_artist(art, layer)
self.BM.add_artist(art)

def setlim(*args, **kwargs):
figax.set_position(getpos(self.ax.get_position())["rect"])
Expand Down Expand Up @@ -3026,17 +3031,31 @@ def show_layer(self, name):

def redraw(self):
"""
Force a re-draw of all cached layers.
Force a re-draw of all cached background layers.
This will make sure that actions not managed by EOmaps are also properly drawn.
- Use this at the very end of your code to trigger a final re-draw!
NOTE: Don't use this in an interactive context since it will trigger a re-draw
Note
----
Don't use this in an interactive context since it will trigger a re-draw
of all background-layers!
To make an artist dynamically updated if you interact with the map, use:
>>> m.BM.add_artist(artist)
"""

self.BM._refetch_bg = True
self.BM.canvas.draw()

@wraps(GridSpec.update)
def subplots_adjust(self, **kwargs):
self.parent.figure.gridspec.update(**kwargs)
# after changing margins etc. a redraw is required
# to fetch the updated background!
self.redraw()


class MapsGrid:
"""
Expand Down Expand Up @@ -3484,3 +3503,7 @@ def savefig(self, *args, **kwargs):
@wraps(Maps.util)
def util(self):
return self.parent.util

@wraps(Maps.subplots_adjust)
def subplots_adjust(self, **kwargs):
return self.parent.subplots_adjust(**kwargs)
11 changes: 10 additions & 1 deletion eomaps/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,13 @@ def bg_layer(self, val):
self._clear_temp_artists("on_layer_change")
# self.fetch_bg(self._bg_layer)

def _refetch_layer(self, layer):
if layer == "all":
self._refetch_bg = True
else:
if layer in self._bg_layers:
del self._bg_layers[layer]

def fetch_bg(self, layer=None, bbox=None, overlay=None):
# add this to the zorder of the overlay-artists prior to plotting
# to ensure that they appear on top of other artists
Expand Down Expand Up @@ -897,7 +904,8 @@ def on_draw(self, event):
# reset all background-layers and re-fetch the default one
if self._refetch_bg:
self._bg_layers = dict()
self.fetch_bg()
if self.bg_layer not in self._bg_layers:
self.fetch_bg()

# workaround for nbagg backend to avoid glitches
# it's slow but at least it works...
Expand Down Expand Up @@ -964,6 +972,7 @@ def add_bg_artist(self, art, layer=0):
else:
# art.set_animated(True)
self._bg_artists[layer].append(art)
self._m.BM._refetch_layer(layer)

def remove_bg_artist(self, art, layer=None):
if layer is None:
Expand Down
26 changes: 25 additions & 1 deletion eomaps/scalebar.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ScaleBar:
def __init__(
self,
m,
preset=None,
scale=None,
autoscale_fraction=0.25,
auto_position=(0.75, 0.25),
Expand Down Expand Up @@ -51,6 +52,11 @@ def __init__(
azim : float
The azimuth-direction (in degrees) in which the scalebar points.
The default is 90.
preset : str
The name of the style preset to use.
- "bw" : a simple black-and white ruler without a background patch
scale : float or None, optional
The distance of the individual segments of the scalebar.
Expand Down Expand Up @@ -166,6 +172,21 @@ def __init__(

self._artists = OrderedDict(patch=None, scale=None)

self.preset = preset

self._picker_name = None

def apply_preset(self, preset):
if preset == "bw":
self.set_scale_props(n=10, width=4, colors=("k", "w"))
self.set_patch_props(fc="none", ec="none", offsets=(1, 1.6, 1, 1))
self.set_label_props(
scale=1.5, offset=0.5, every=2, weight="bold", family="Courier New"
)
self._autoscale = 0.5
self._estimate_scale()
self.set_position()

def _estimate_scale(self):
try:
ax2data = self._m.ax.transAxes + self._m.ax.transData.inverted()
Expand Down Expand Up @@ -662,6 +683,9 @@ def _add_scalebar(self, lon, lat, azim):
coll.set_linewidth(self._scale_props["width"])
self._artists["scale"] = self._m.figure.ax.add_collection(coll, autolim=False)

# apply preset
self.apply_preset(self.preset)

# -------------- make all artists animated
self._artists["scale"].set_zorder(1)
self._artists["patch"].set_zorder(0)
Expand Down Expand Up @@ -831,7 +855,7 @@ def addcbs(self, s, **kwargs):
if not hasattr(s, "_cid_remove"):
s._cid_remove = self.cb.keypress.attach(scb_remove, key="delete", s=s)

def scb_unpick(self, s, **kwargs):
def scb_unpick(m, s, **kwargs):
s._remove_callbacks()

self._picker_name = f"_scalebar{len(self._existing_pickers)}"
Expand Down
Loading

0 comments on commit 0819d7d

Please sign in to comment.