Skip to content
Merged
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Merge from master, repair conflicts
  • Loading branch information
lukelbd committed Sep 15, 2019
commit 4be37c4aea9788d13cb20fada04618f223740706
213 changes: 33 additions & 180 deletions proplot/axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,14 @@ def _disable_decorator(msg):
def decorator(func):
@functools.wraps(func)
def wrapper(self, *args, **kwargs):
raise RuntimeError(msg)
raise RuntimeError(msg.format(func.__name__))
wrapper.__doc__ = None
return wrapper
return decorator

#-----------------------------------------------------------------------------#
# Generalized custom axes class
#-----------------------------------------------------------------------------#
_disabled_log_err = RuntimeError('Redundant function has been disabled. Control axis scale with e.g. ax.format(xscale="log", yscale="log").')
class Axes(maxes.Axes):
"""Lowest-level axes subclass. Handles titles and axis
sharing. Adds several new methods and overrides existing ones."""
Expand Down Expand Up @@ -310,7 +309,7 @@ def _loc_translate(loc, **kwargs):
if loc is True:
loc = None
elif isinstance(loc, (str, Integral)):
loc = LOC_TRANSLATE.get(loc, loc) # may still be invalid
loc = LOC_TRANSLATE.get(loc, loc)
return loc

def _make_inset_locator(self, bounds, trans):
Expand Down Expand Up @@ -1171,7 +1170,7 @@ def legend(self, *args, loc=None, width=None, space=None, **kwargs):

def draw(self, renderer=None, *args, **kwargs):
"""Adds post-processing steps before axes is drawn."""
self._hide_labels() # hide axis labels or meridian parallel labels
self._hide_labels()
self._reassign_title()
super().draw(renderer, *args, **kwargs)

Expand Down Expand Up @@ -1376,11 +1375,11 @@ def _iter_panels(self, sides='lrbt'):
text = wrappers._text_wrapper(maxes.Axes.text)

# Disabled methods
_disable = _disable_decorator('Redundant method has been disabled. Instead, use ax.plot() and set logarithmic axis scales with ax.format(xscale="log", yscale="log").')
_disable = _disable_decorator('Redundant plotting method {!r} has been disabled. Instead, use ax.plot() and set logarithmic axis scales with ax.format(xscale="log", yscale="log").')
loglog = _disable(maxes.Axes.loglog)
semilogx = _disable(maxes.Axes.semilogx)
semilogy = _disable(maxes.Axes.semilogy)
_disable = _disable_decorator('Redundant method has been disabled. Instead, pass np.datetime64 or datetime.datetime data to ax.plot(), and datetime axes will be enabled automatically.')
_disable = _disable_decorator('Redundant plotting method {!r} has been disabled. Instead, pass np.datetime64 or datetime.datetime data to ax.plot(), and datetime axes will be enabled automatically.')
plot_date = _disable(maxes.Axes.plot_date)

#-----------------------------------------------------------------------------#
Expand Down Expand Up @@ -2312,167 +2311,7 @@ def twiny(self):
sharing. Returns a `CartesianAxes` instance."""
return self.altx()

class ProjectionAxes(Axes):
"""Intermediate class, shared by `CartopyAxes` and
`BasemapAxes`. Disables methods that are inappropriate for map
projections and adds `ProjectionAxes.format`, so that arguments
passed to `Axes.format` are identical for `CartopyAxes`
and `BasemapAxes`."""
def __init__(self, *args, **kwargs): # just to disable docstring inheritence
"""
See also
--------
`~proplot.subplots.subplots`, `CartopyAxes`, `BasemapAxes`
"""
# Store props that let us dynamically and incrementally modify
# line locations and settings like with Cartesian axes
self._boundinglat = None
self._latmax = None
self._latlines = None
self._lonlines = None
self._lonlines_values = None
self._latlines_values = None
self._lonlines_labels = None
self._latlines_labels = None
super().__init__(*args, **kwargs)

def _hide_labels(self):
"""Hides meridian and parallel labels for simple "rectangular"
projections."""
# TODO: Write this!

def _projection_format_kwargs(self, *,
lonlim=None, latlim=None, boundinglat=None, grid=None,
lonlines=None, lonlocator=None,
latlines=None, latlocator=None, latmax=None,
labels=None, latlabels=None, lonlabels=None,
**kwargs,
):
# Parse alternative keyword args
lonlines = _notNone(lonlines, lonlocator, rc['geogrid.lonstep'], names=('lonlines', 'lonlocator'))
latlines = _notNone(latlines, latlocator, rc['geogrid.latstep'], names=('latlines', 'latlocator'))
latmax = _notNone(latmax, rc['geogrid.latmax'])
labels = _notNone(labels, rc['geogrid.labels'])
grid = _notNone(grid, rc['geogrid'])
if labels:
lonlabels = _notNone(lonlabels, 1)
latlabels = _notNone(latlabels, 1)

# Longitude gridlines, draw relative to projection prime meridian
# NOTE: Always generate gridlines array at least on first format call
# because rc setting will be not None
if isinstance(self, CartopyAxes):
lon_0 = self.projection.proj4_params.get('lon_0', 0)
else:
base = 5
lon_0 = base*round(self.projection.lonmin/base) + 180 # central longitude
if lonlines is not None:
if not np.iterable(lonlines):
lonlines = utils.arange(lon_0 - 180, lon_0 + 180, lonlines)
lonlines = [*lonlines]

# Latitudes gridlines, draw from -latmax to latmax unless result would
# be asymmetrical across equator
# NOTE: Basemap axes redraw *meridians* if they detect latmax was
# explicitly changed, so important not to overwrite 'latmax' variable
# with default value! Just need it for this calculation, then when
# drawparallels is called will use self._latmax
if latlines is not None or latmax is not None:
# Fill defaults
if latlines is None:
latlines = _notNone(self._latlines_values, rc.get('geogrid.latstep'))
ilatmax = _notNone(latmax, self._latmax, rc.get('geogrid.latmax'))
# Get tick locations
if not np.iterable(latlines):
if (ilatmax % latlines) == (-ilatmax % latlines):
latlines = utils.arange(-ilatmax, ilatmax, latlines)
else:
latlines = utils.arange(0, ilatmax, latlines)
if latlines[-1] != ilatmax:
latlines = np.concatenate((latlines, [ilatmax]))
latlines = np.concatenate((-latlines[::-1], latlines[1:]))
latlines = [*latlines]

# Length-4 boolean arrays of whether and where to toggle labels
# Format is [left, right, bottom, top]
lonarray, latarray = [], []
for labs,array in zip((lonlabels,latlabels), (lonarray,latarray)):
if labs is None:
continue # leave empty
if isinstance(labs, str):
string = labs
labs = [0]*4
for idx,char in zip([0,1,2,3],'lrbt'):
if char in string:
labs[idx] = 1
elif not np.iterable(labs):
labs = np.atleast_1d(labs)
if len(labs) == 1:
labs = [*labs, 0] # default is to label bottom/left
if len(labs) == 2:
if array is lonarray:
labs = [0, 0, *labs]
else:
labs = [*labs, 0, 0]
elif len(labs) != 4:
raise ValueError(f'Invalid lon/lat label spec: {labs}.')
array[:] = labs
lonarray = lonarray or None # None so use default locations
latarray = latarray or None

# Add attributes for redrawing lines
if latmax is not None:
self._latmax = latmax
if latlines is not None:
self._latlines_values = latlines
if lonlines is not None:
self._lonlines_values = lonlines
if latarray is not None:
self._latlines_labels = latarray
if lonarray is not None:
self._lonlines_labels = lonarray

# Grid toggling, must come after everything else in case e.g.
# rc.geogrid is False but user passed grid=True so we need to
# recover the *default* lonlines and latlines values
if grid is not None:
if not grid:
lonlines = latlines = []
else:
lonlines = self._lonlines_values
latlines = self._latlines_values

return (latmax, lonlim, latlim, boundinglat,
lonlines, latlines, lonarray, latarray, kwargs)

# Disabled methods suitable only for cartesian axes
_disable = _disable_decorator('Invalid plotting method for map projection or polar axes.')
twinx = _disable(Axes.twinx)
twiny = _disable(Axes.twiny)
matshow = _disable(Axes.matshow)
imshow = _disable(Axes.imshow)
spy = _disable(Axes.spy)
hist = _disable(Axes.hist)
hist2d = _disable(Axes.hist2d)
boxplot = _disable(Axes.boxplot)
violinplot = _disable(Axes.violinplot)
step = _disable(Axes.step)
stem = _disable(Axes.stem)
stackplot = _disable(Axes.stackplot)
table = _disable(Axes.table)
eventplot = _disable(Axes.eventplot)
pie = _disable(Axes.pie)
xcorr = _disable(Axes.xcorr)
acorr = _disable(Axes.acorr)
psd = _disable(Axes.psd)
csd = _disable(Axes.csd)
cohere = _disable(Axes.cohere)
specgram = _disable(Axes.specgram)
angle_spectrum = _disable(Axes.angle_spectrum)
phase_spectrum = _disable(Axes.phase_spectrum)
magnitude_spectrum = _disable(Axes.magnitude_spectrum)

class PolarAxes(ProjectionAxes, mproj.PolarAxes):
class PolarAxes(Axes, mproj.PolarAxes):
"""Intermediate class, mixes `ProjectionAxes` with
`~matplotlib.projections.polar.PolarAxes`."""
name = 'polar2'
Expand Down Expand Up @@ -2730,19 +2569,6 @@ def __init__(self, *args, **kwargs): # just to disable docstring inheritence
self._latlines_labels = None
super().__init__(*args, **kwargs)

@wrappers._expand_methods_list
def __getattribute__(self, attr, *args):
"""Disables the methods `MAP_DISABLED_METHODS`, which are
inappropriate for map projections."""
if attr in wrappers.MAP_DISABLED_METHODS:
raise AttributeError(f'Invalid plotting function {attr!r} for map projection axes.')
return super().__getattribute__(attr, *args)

def _hide_labels(self):
"""Hides meridian and parallel labels for simple "rectangular"
projections."""
# TODO: Write this!

def format(self, *,
lonlim=None, latlim=None, boundinglat=None, grid=None,
lonlines=None, lonlocator=None,
Expand Down Expand Up @@ -2900,6 +2726,33 @@ def format(self, *,
lonlines, latlines, latmax, lonarray, latarray)
super().format(**kwargs)

# Disabled methods suitable only for cartesian axes
_disable = _disable_decorator('Invalid plotting method {!r} for map projection axes.')
twinx = _disable(Axes.twinx)
twiny = _disable(Axes.twiny)
matshow = _disable(Axes.matshow)
imshow = _disable(Axes.imshow)
spy = _disable(Axes.spy)
hist = _disable(Axes.hist)
hist2d = _disable(Axes.hist2d)
boxplot = _disable(Axes.boxplot)
violinplot = _disable(Axes.violinplot)
step = _disable(Axes.step)
stem = _disable(Axes.stem)
stackplot = _disable(Axes.stackplot)
table = _disable(Axes.table)
eventplot = _disable(Axes.eventplot)
pie = _disable(Axes.pie)
xcorr = _disable(Axes.xcorr)
acorr = _disable(Axes.acorr)
psd = _disable(Axes.psd)
csd = _disable(Axes.csd)
cohere = _disable(Axes.cohere)
specgram = _disable(Axes.specgram)
angle_spectrum = _disable(Axes.angle_spectrum)
phase_spectrum = _disable(Axes.phase_spectrum)
magnitude_spectrum = _disable(Axes.magnitude_spectrum)

# Cartopy takes advantage of documented feature where any class with method
# named _as_mpl_axes can be passed as 'projection' object.
# Feature documented here: https://matplotlib.org/devel/add_new_projection.html
Expand Down
You are viewing a condensed version of this merge commit. You can view the full changes here.