holoviz / geoviews

Simple, concise geographical visualization in Python
http://geoviews.org
BSD 3-Clause "New" or "Revised" License
595 stars 77 forks source link

Adding colorbar raises LinAlgError #598

Closed stefmolin closed 1 year ago

stefmolin commented 1 year ago

ALL software version info

(this library, plus any other relevant software, e.g. bokeh, python, notebook, OS, browser, etc)

Complete, minimal, self-contained example code that reproduces the issue

import geopandas as gpd
import pandas as pd

import geoviews as gv
import geoviews.feature as gf
import holoviews as hv

gv.extension('matplotlib')

file = 'https://raw.githubusercontent.com/stefmolin/python-data-viz-workshop/main/data/earthquakes.geojson'
earthquakes = gpd.read_file(file).assign(
    time=lambda x: pd.to_datetime(x.time, unit='ms'),
    month=lambda x: x.time.dt.month
)[['geometry', 'mag', 'time', 'month']]

points = gv.Points(
    earthquakes.query('month == 1'),
    kdims=['longitude', 'latitude'],
    vdims=['mag']
).redim.range(mag=(-2, 10), latitude=(-90, 90))

(
    gf.land * gf.coastline * gf.borders * points
).opts(
    gv.opts.Points(color='mag', cmap='fire_r', colorbar=True, alpha=0.75),
    gv.opts.Overlay(
        global_extent=False, title='earthquakes', fontscale=2
    )
).opts(
    fig_inches=(6, 3), aspect=2, fig_size=250, fig_bounds=(0.07, 0.05, 0.87, 0.95)
)

Removing colorbar=True works. This code worked before I updated to matplotlib 3.6.0 and cartopy 0.21.0.

Stack traceback and/or browser JavaScript console output

---------------------------------------------------------------------------
LinAlgError                               Traceback (most recent call last)
File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/IPython/core/formatters.py:973, in MimeBundleFormatter.__call__(self, obj, include, exclude)
    970     method = get_real_method(obj, self.print_method)
    972     if method is not None:
--> 973         return method(include=include, exclude=exclude)
    974     return None
    975 else:

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/core/dimension.py:1316, in Dimensioned._repr_mimebundle_(self, include, exclude)
   1309 def _repr_mimebundle_(self, include=None, exclude=None):
   1310     """
   1311     Resolves the class hierarchy for the class rendering the
   1312     object using any display hooks registered on Store.display
   1313     hooks.  The output of all registered display_hooks is then
   1314     combined and returned.
   1315     """
-> 1316     return Store.render(self)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/core/options.py:1405, in Store.render(cls, obj)
   1403 data, metadata = {}, {}
   1404 for hook in hooks:
-> 1405     ret = hook(obj)
   1406     if ret is None:
   1407         continue

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/ipython/display_hooks.py:282, in pprint_display(obj)
    280 if not ip.display_formatter.formatters['text/plain'].pprint:
    281     return None
--> 282 return display(obj, raw_output=True)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/ipython/display_hooks.py:252, in display(obj, raw_output, **kwargs)
    250 elif isinstance(obj, (CompositeOverlay, ViewableElement)):
    251     with option_state(obj):
--> 252         output = element_display(obj)
    253 elif isinstance(obj, (Layout, NdLayout, AdjointLayout)):
    254     with option_state(obj):

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/ipython/display_hooks.py:146, in display_hook.<locals>.wrapped(element)
    144 try:
    145     max_frames = OutputSettings.options['max_frames']
--> 146     mimebundle = fn(element, max_frames=max_frames)
    147     if mimebundle is None:
    148         return {}, {}

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/ipython/display_hooks.py:192, in element_display(element, max_frames)
    189 if type(element) not in Store.registry[backend]:
    190     return None
--> 192 return render(element)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/ipython/display_hooks.py:68, in render(obj, **kwargs)
     65 if renderer.fig == 'pdf':
     66     renderer = renderer.instance(fig='png')
---> 68 return renderer.components(obj, **kwargs)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/plotting/renderer.py:450, in Renderer.components(self, obj, fmt, comm, **kwargs)
    448     return data, {}
    449 else:
--> 450     html = self._figure_data(plot, fmt, as_script=True, **kwargs)
    451 data['text/html'] = html
    453 return (data, {MIME_TYPES['jlab-hv-exec']: metadata})

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/holoviews/plotting/mpl/renderer.py:175, in MPLRenderer._figure_data(self, plot, fmt, bbox_inches, as_script, **kwargs)
    173         pass
    174     bytes_io = BytesIO()
--> 175     fig.canvas.print_figure(bytes_io, **kw)
    176     data = bytes_io.getvalue()
    178 if as_script:

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/backend_bases.py:2338, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2334 try:
   2335     # _get_renderer may change the figure dpi (as vector formats
   2336     # force the figure dpi to 72), so we need to set it again here.
   2337     with cbook._setattr_cm(self.figure, dpi=dpi):
-> 2338         result = print_method(
   2339             filename,
   2340             facecolor=facecolor,
   2341             edgecolor=edgecolor,
   2342             orientation=orientation,
   2343             bbox_inches_restore=_bbox_inches_restore,
   2344             **kwargs)
   2345 finally:
   2346     if bbox_inches and restore_bbox:

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/backend_bases.py:2204, in FigureCanvasBase._switch_canvas_and_return_print_method.<locals>.<lambda>(*args, **kwargs)
   2200     optional_kws = {  # Passed by print_figure for other renderers.
   2201         "dpi", "facecolor", "edgecolor", "orientation",
   2202         "bbox_inches_restore"}
   2203     skip = optional_kws - {*inspect.signature(meth).parameters}
-> 2204     print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(
   2205         *args, **{k: v for k, v in kwargs.items() if k not in skip}))
   2206 else:  # Let third-parties do as they see fit.
   2207     print_method = meth

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/_api/deprecation.py:410, in delete_parameter.<locals>.wrapper(*inner_args, **inner_kwargs)
    400     deprecation_addendum = (
    401         f"If any parameter follows {name!r}, they should be passed as "
    402         f"keyword, not positionally.")
    403     warn_deprecated(
    404         since,
    405         name=repr(name),
   (...)
    408                  else deprecation_addendum,
    409         **kwargs)
--> 410 return func(*inner_args, **inner_kwargs)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:520, in FigureCanvasAgg.print_png(self, filename_or_obj, metadata, pil_kwargs, *args)
    471 @_api.delete_parameter("3.5", "args")
    472 def print_png(self, filename_or_obj, *args,
    473               metadata=None, pil_kwargs=None):
    474     """
    475     Write the figure to a PNG file.
    476 
   (...)
    518         *metadata*, including the default 'Software' key.
    519     """
--> 520     self._print_pil(filename_or_obj, "png", pil_kwargs, metadata)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:466, in FigureCanvasAgg._print_pil(self, filename_or_obj, fmt, pil_kwargs, metadata)
    461 def _print_pil(self, filename_or_obj, fmt, pil_kwargs, metadata=None):
    462     """
    463     Draw the canvas, then save it using `.image.imsave` (to which
    464     *pil_kwargs* and *metadata* are forwarded).
    465     """
--> 466     FigureCanvasAgg.draw(self)
    467     mpl.image.imsave(
    468         filename_or_obj, self.buffer_rgba(), format=fmt, origin="upper",
    469         dpi=self.figure.dpi, metadata=metadata, pil_kwargs=pil_kwargs)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:408, in FigureCanvasAgg.draw(self)
    404 # Acquire a lock on the shared font cache.
    405 with RendererAgg.lock, \
    406      (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
    407       else nullcontext()):
--> 408     self.figure.draw(self.renderer)
    409     # A GUI class may be need to update a window using this draw, so
    410     # don't forget to call the superclass.
    411     super().draw()

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/artist.py:74, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
     72 @wraps(draw)
     73 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 74     result = draw(artist, renderer, *args, **kwargs)
     75     if renderer._rasterizing:
     76         renderer.stop_rasterizing()

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     48     if artist.get_agg_filter() is not None:
     49         renderer.start_filter()
---> 51     return draw(artist, renderer)
     52 finally:
     53     if artist.get_agg_filter() is not None:

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/figure.py:3069, in Figure.draw(self, renderer)
   3066         # ValueError can occur when resizing a window.
   3068 self.patch.draw(renderer)
-> 3069 mimage._draw_list_compositing_images(
   3070     renderer, self, artists, self.suppressComposite)
   3072 for sfig in self.subfigs:
   3073     sfig.draw(renderer)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/image.py:131, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    129 if not_composite or not has_images:
    130     for a in artists:
--> 131         a.draw(renderer)
    132 else:
    133     # Composite any adjacent images together
    134     image_group = []

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     48     if artist.get_agg_filter() is not None:
     49         renderer.start_filter()
---> 51     return draw(artist, renderer)
     52 finally:
     53     if artist.get_agg_filter() is not None:

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/cartopy/mpl/geoaxes.py:538, in GeoAxes.draw(self, renderer, **kwargs)
    533         self.imshow(img, extent=extent, origin=origin,
    534                     transform=factory.crs, *factory_args[1:],
    535                     **factory_kwargs)
    536 self._done_img_factory = True
--> 538 return super().draw(renderer=renderer, **kwargs)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/artist.py:51, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     48     if artist.get_agg_filter() is not None:
     49         renderer.start_filter()
---> 51     return draw(artist, renderer)
     52 finally:
     53     if artist.get_agg_filter() is not None:

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/axes/_base.py:3070, in _AxesBase.draw(self, renderer)
   3067     for spine in self.spines.values():
   3068         artists.remove(spine)
-> 3070 self._update_title_position(renderer)
   3072 if not self.axison:
   3073     for _axis in self._axis_map.values():

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/cartopy/mpl/geoaxes.py:562, in GeoAxes._update_title_position(self, renderer)
    559 if top < 0:
    560     # nothing to do if no label found
    561     return
--> 562 yn = self.transAxes.inverted().transform((0., top))[1]
    563 if yn <= 1:
    564     # nothing to do if the upper bounds of labels is below
    565     # the top of the axes
    566     return

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/matplotlib/transforms.py:1876, in Affine2DBase.inverted(self)
   1874     if self._shorthand_name:
   1875         shorthand_name = '(%s)-1' % self._shorthand_name
-> 1876     self._inverted = Affine2D(inv(mtx), shorthand_name=shorthand_name)
   1877     self._invalid = 0
   1878 return self._inverted

File <__array_function__ internals>:180, in inv(*args, **kwargs)

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/numpy/linalg/linalg.py:545, in inv(a)
    543 signature = 'D->D' if isComplexType(t) else 'd->d'
    544 extobj = get_linalg_error_extobj(_raise_linalgerror_singular)
--> 545 ainv = _umath_linalg.inv(a, signature=signature, extobj=extobj)
    546 return wrap(ainv.astype(result_t, copy=False))

File ~/miniconda3/envs/data_viz_workshop/lib/python3.9/site-packages/numpy/linalg/linalg.py:88, in _raise_linalgerror_singular(err, flag)
     87 def _raise_linalgerror_singular(err, flag):
---> 88     raise LinAlgError("Singular matrix")

LinAlgError: Singular matrix
stefmolin commented 1 year ago

Updating to holoviews 1.15.1 fixed this.