holoviz / geoviews

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

`datashade=True` does not work with `geo=True` after upgrading to `1.9.4` #563

Closed keewis closed 2 years ago

keewis commented 2 years ago

I'm not sure whether to report this to holoviews, hvplot, datashader or here, but given that geo=False does not raise and downgrading geoviews works around this bug I'll open this here.

ALL software version info

python: 3.9.10
xarray: 0.21.1
hvplot: 0.7.3
ipython: 8.1.1

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

with geoviews=1.9.3 and holoviews=1.14.7:

In [1]: import numpy as np
   ...: import xarray as xr
   ...: import hvplot.xarray
   ...: 
   ...: lat, lon = np.meshgrid(np.linspace(48.8, 50.2, 500), np.linspace(-7, -5, 500))
   ...: arr = xr.DataArray(np.ones((500, 500)), coords={"lat": (("y", "x"), lat), "lon": (("y", "x"), lon)}, dims=("y", "x"))
   ...: arr.hvplot.quadmesh(x="lon", y="lat", datashade=True, geo=True)
Out[1]: 
:DynamicMap   []
   :RGB   [lon,lat]   (R,G,B,A)

(the size of the data is not important)

with geoviews=1.9.4 and holoviews=1.14.7 or holoviews=1.14.8:

In [1]: import numpy as np
   ...: import xarray as xr
   ...: import hvplot.xarray
   ...: 
   ...: lat, lon = np.meshgrid(np.linspace(48.8, 50.2, 10), np.linspace(-7, -5, 10))
   ...: arr = xr.DataArray(np.ones((10, 10)), coords={"lat": (("y", "x"), lat), "lon": (("y", "x"), lon)}, dims=("y", "x"))
   ...: arr.hvplot.quadmesh(x="lon", y="lat", datashade=True, geo=True)
Out[1]: WARNING:param.dynamic_operation: Callable raised "DataError("The coordinates on the 'R' DataArray do not match the provided key dimensions (kdims). The following coords were left unspecified: ['band']. If you are requesting a lower dimensional view such as a histogram cast the xarray to a columnar format using the .to_dataframe or .to_dask_dataframe methods before providing it to HoloViews.")".
Invoked as dynamic_operation(height=400, scale=1.0, width=400, x_range=None, y_range=None)
WARNING:param.dynamic_operation: Callable raised "DataError("The coordinates on the 'R' DataArray do not match the provided key dimensions (kdims). The following coords were left unspecified: ['band']. If you are requesting a lower dimensional view such as a histogram cast the xarray to a columnar format using the .to_dataframe or .to_dask_dataframe methods before providing it to HoloViews.")".
Invoked as dynamic_operation(height=400, scale=1.0, width=400, x_range=None, y_range=None)
---------------------------------------------------------------------------
DataError                                 Traceback (most recent call last)
File .../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 .../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 .../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 .../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 .../lib/python3.9/site-packages/holoviews/ipython/display_hooks.py:258, in display(obj, raw_output, **kwargs)
    256 elif isinstance(obj, (HoloMap, DynamicMap)):
    257     with option_state(obj):
--> 258         output = map_display(obj)
    259 elif isinstance(obj, Plot):
    260     output = render(obj)

File .../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 .../lib/python3.9/site-packages/holoviews/ipython/display_hooks.py:206, in map_display(vmap, max_frames)
    203     max_frame_warning(max_frames)
    204     return None
--> 206 return render(vmap)

File .../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 .../lib/python3.9/site-packages/holoviews/plotting/renderer.py:410, in Renderer.components(self, obj, fmt, comm, **kwargs)
    408 doc = Document()
    409 with config.set(embed=embed):
--> 410     model = plot.layout._render_model(doc, comm)
    411 if embed:
    412     return render_model(model, comm)

File .../lib/python3.9/site-packages/panel/viewable.py:455, in Renderable._render_model(self, doc, comm)
    453 if comm is None:
    454     comm = state._comm_manager.get_server_comm()
--> 455 model = self.get_root(doc, comm)
    457 if config.embed:
    458     embed_state(self, model, doc,
    459                 json=config.embed_json,
    460                 json_prefix=config.embed_json_prefix,
    461                 save_path=config.embed_save_path,
    462                 load_path=config.embed_load_path,
    463                 progress=False)

File .../lib/python3.9/site-packages/panel/viewable.py:512, in Renderable.get_root(self, doc, comm, preprocess)
    495 """
    496 Returns the root model and applies pre-processing hooks
    497 
   (...)
    509 Returns the bokeh model corresponding to this panel object
    510 """
    511 doc = init_doc(doc)
--> 512 root = self._get_model(doc, comm=comm)
    513 if preprocess:
    514     self._preprocess(root)

File .../lib/python3.9/site-packages/panel/layout/base.py:122, in Panel._get_model(self, doc, root, parent, comm)
    120 if root is None:
    121     root = model
--> 122 objects = self._get_objects(model, [], doc, root, comm)
    123 props = dict(self._init_params(), objects=objects)
    124 model.update(**self._process_param_change(props))

File .../lib/python3.9/site-packages/panel/layout/base.py:112, in Panel._get_objects(self, model, old_objects, doc, root, comm)
    110 else:
    111     try:
--> 112         child = pane._get_model(doc, root, model, comm)
    113     except RerenderError:
    114         return self._get_objects(model, current_objects[:i], doc, root, comm)

File .../lib/python3.9/site-packages/panel/pane/holoviews.py:239, in HoloViews._get_model(self, doc, root, parent, comm)
    237     plot = self.object
    238 else:
--> 239     plot = self._render(doc, comm, root)
    241 plot.pane = self
    242 backend = plot.renderer.backend

File .../lib/python3.9/site-packages/panel/pane/holoviews.py:312, in HoloViews._render(self, doc, comm, root)
    309     if comm:
    310         kwargs['comm'] = comm
--> 312 return renderer.get_plot(self.object, **kwargs)

File .../lib/python3.9/site-packages/holoviews/plotting/bokeh/renderer.py:73, in BokehRenderer.get_plot(self_or_cls, obj, doc, renderer, **kwargs)
     66 @bothmethod
     67 def get_plot(self_or_cls, obj, doc=None, renderer=None, **kwargs):
     68     """
     69     Given a HoloViews Viewable return a corresponding plot instance.
     70     Allows supplying a document attach the plot to, useful when
     71     combining the bokeh model with another plot.
     72     """
---> 73     plot = super(BokehRenderer, self_or_cls).get_plot(obj, doc, renderer, **kwargs)
     74     if plot.document is None:
     75         plot.document = Document() if self_or_cls.notebook_context else curdoc()

File .../lib/python3.9/site-packages/holoviews/plotting/renderer.py:220, in Renderer.get_plot(self_or_cls, obj, doc, renderer, comm, **kwargs)
    217     raise SkipRendering(msg.format(dims=dims))
    219 # Initialize DynamicMaps with first data item
--> 220 initialize_dynamic(obj)
    222 if not renderer:
    223     renderer = self_or_cls

File .../lib/python3.9/site-packages/holoviews/plotting/util.py:254, in initialize_dynamic(obj)
    252     continue
    253 if not len(dmap):
--> 254     dmap[dmap._initial_key()]

File .../lib/python3.9/site-packages/holoviews/core/spaces.py:1344, in DynamicMap.__getitem__(self, key)
   1342 # Not a cross product and nothing cached so compute element.
   1343 if cache is not None: return cache
-> 1344 val = self._execute_callback(*tuple_key)
   1345 if data_slice:
   1346     val = self._dataslice(val, data_slice)

File .../lib/python3.9/site-packages/holoviews/core/spaces.py:1111, in DynamicMap._execute_callback(self, *args)
   1108     kwargs['_memoization_hash_'] = hash_items
   1110 with dynamicmap_memoization(self.callback, self.streams):
-> 1111     retval = self.callback(*args, **kwargs)
   1112 return self._style(retval)

File .../lib/python3.9/site-packages/holoviews/core/spaces.py:708, in Callable.__call__(self, *args, **kwargs)
    705     args, kwargs = (), dict(pos_kwargs, **kwargs)
    707 try:
--> 708     ret = self.callable(*args, **kwargs)
    709 except KeyError:
    710     # KeyError is caught separately because it is used to signal
    711     # invalid keys on DynamicMap and should not warn
    712     raise

File .../lib/python3.9/site-packages/holoviews/util/__init__.py:1043, in Dynamic._dynamic_operation.<locals>.dynamic_operation(*key, **kwargs)
   1042 def dynamic_operation(*key, **kwargs):
-> 1043     key, obj = resolve(key, kwargs)
   1044     return apply(obj, *key, **kwargs)

File .../lib/python3.9/site-packages/holoviews/util/__init__.py:1032, in Dynamic._dynamic_operation.<locals>.resolve(key, kwargs)
   1030 elif isinstance(map_obj, DynamicMap) and map_obj._posarg_keys and not key:
   1031     key = tuple(kwargs[k] for k in map_obj._posarg_keys)
-> 1032 return key, map_obj[key]

File .../lib/python3.9/site-packages/holoviews/core/spaces.py:1344, in DynamicMap.__getitem__(self, key)
   1342 # Not a cross product and nothing cached so compute element.
   1343 if cache is not None: return cache
-> 1344 val = self._execute_callback(*tuple_key)
   1345 if data_slice:
   1346     val = self._dataslice(val, data_slice)

File .../lib/python3.9/site-packages/holoviews/core/spaces.py:1111, in DynamicMap._execute_callback(self, *args)
   1108     kwargs['_memoization_hash_'] = hash_items
   1110 with dynamicmap_memoization(self.callback, self.streams):
-> 1111     retval = self.callback(*args, **kwargs)
   1112 return self._style(retval)

File .../lib/python3.9/site-packages/holoviews/core/spaces.py:708, in Callable.__call__(self, *args, **kwargs)
    705     args, kwargs = (), dict(pos_kwargs, **kwargs)
    707 try:
--> 708     ret = self.callable(*args, **kwargs)
    709 except KeyError:
    710     # KeyError is caught separately because it is used to signal
    711     # invalid keys on DynamicMap and should not warn
    712     raise

File .../lib/python3.9/site-packages/holoviews/util/__init__.py:1044, in Dynamic._dynamic_operation.<locals>.dynamic_operation(*key, **kwargs)
   1042 def dynamic_operation(*key, **kwargs):
   1043     key, obj = resolve(key, kwargs)
-> 1044     return apply(obj, *key, **kwargs)

File .../lib/python3.9/site-packages/holoviews/util/__init__.py:1036, in Dynamic._dynamic_operation.<locals>.apply(element, *key, **kwargs)
   1034 def apply(element, *key, **kwargs):
   1035     kwargs = dict(util.resolve_dependent_kwargs(self.p.kwargs), **kwargs)
-> 1036     processed = self._process(element, key, kwargs)
   1037     if (self.p.link_dataset and isinstance(element, Dataset) and
   1038         isinstance(processed, Dataset) and processed._dataset is None):
   1039         processed._dataset = element.dataset

File .../lib/python3.9/site-packages/holoviews/util/__init__.py:1018, in Dynamic._process(self, element, key, kwargs)
   1016 elif isinstance(self.p.operation, Operation):
   1017     kwargs = {k: v for k, v in kwargs.items() if k in self.p.operation.param}
-> 1018     return self.p.operation.process_element(element, key, **kwargs)
   1019 else:
   1020     return self.p.operation(element, **kwargs)

File .../lib/python3.9/site-packages/holoviews/core/operation.py:194, in Operation.process_element(self, element, key, **params)
    191 else:
    192     self.p = param.ParamOverrides(self, params,
    193                                   allow_extra_keywords=self._allow_extra_keywords)
--> 194 return self._apply(element, key)

File .../lib/python3.9/site-packages/holoviews/core/operation.py:152, in Operation._apply(self, element, key)
    147         Store.transfer_options(
    148             element, ret, backend, self._transfer_options, level=1
    149         )
    151 for hook in self._postprocess_hooks:
--> 152     ret = hook(self, ret, **kwargs)
    154 if (self._propagate_dataset and isinstance(ret, Dataset)
    155     and isinstance(element, Dataset) and not in_method):
    156     ret._dataset = element.dataset.clone()

File .../lib/python3.9/site-packages/geoviews/operation/__init__.py:54, in add_crs(op, element, **kwargs)
     49 def add_crs(op, element, **kwargs):
     50     """
     51     Converts any elements in the input to their equivalent geotypes
     52     if given a coordinate reference system.
     53     """
---> 54     return element.map(lambda x: convert_to_geotype(x, kwargs.get('crs')), Element)

File .../lib/python3.9/site-packages/holoviews/core/data/__init__.py:207, in PipelineMeta.pipelined.<locals>.pipelined_fn(*args, **kwargs)
    204     inst._in_method = True
    206 try:
--> 207     result = method_fn(*args, **kwargs)
    208     if PipelineMeta.disable:
    209         return result

File .../lib/python3.9/site-packages/holoviews/core/data/__init__.py:1223, in Dataset.map(self, *args, **kwargs)
   1221 @wraps(LabelledData.map)
   1222 def map(self, *args, **kwargs):
-> 1223     return super(Dataset, self).map(*args, **kwargs)

File .../lib/python3.9/site-packages/holoviews/core/dimension.py:710, in LabelledData.map(self, map_fn, specs, clone)
    708     return deep_mapped
    709 else:
--> 710     return map_fn(self) if applies else self

File .../lib/python3.9/site-packages/geoviews/operation/__init__.py:54, in add_crs.<locals>.<lambda>(x)
     49 def add_crs(op, element, **kwargs):
     50     """
     51     Converts any elements in the input to their equivalent geotypes
     52     if given a coordinate reference system.
     53     """
---> 54     return element.map(lambda x: convert_to_geotype(x, kwargs.get('crs')), Element)

File .../lib/python3.9/site-packages/geoviews/operation/__init__.py:28, in convert_to_geotype(element, crs)
     26 if crs is None or geotype is None or isinstance(element, _Element):
     27     return element
---> 28 return geotype(element, crs=crs)

File .../lib/python3.9/site-packages/geoviews/element/geo.py:110, in _Element.__init__(self, data, kdims, vdims, **kwargs)
    108 elif isinstance(data, _Element):
    109     kwargs['crs'] = data.crs
--> 110 super(_Element, self).__init__(data, kdims=kdims, vdims=vdims, **kwargs)

File .../lib/python3.9/site-packages/holoviews/element/raster.py:678, in RGB.__init__(self, data, kdims, vdims, **params)
    672 if ((hasattr(data, 'shape') and data.shape[-1] == 4 and len(vdims) == 3) or
    673     (isinstance(data, tuple) and isinstance(data[-1], np.ndarray) and data[-1].ndim == 3
    674      and data[-1].shape[-1] == 4 and len(vdims) == 3) or
    675     (isinstance(data, dict) and tuple(dimension_name(vd) for vd in vdims)+(alpha.name,) in data)):
    676     # Handle all forms of packed value dimensions
    677     vdims.append(alpha)
--> 678 super(RGB, self).__init__(data, kdims=kdims, vdims=vdims, **params)

File .../lib/python3.9/site-packages/holoviews/element/raster.py:292, in Image.__init__(self, data, kdims, vdims, bounds, extents, xdensity, ydensity, rtol, **params)
    289 else:
    290     params['rtol'] = config.image_rtol
--> 292 Dataset.__init__(self, data, kdims=kdims, vdims=vdims, extents=extents, **params)
    293 if not self.interface.gridded:
    294     raise DataError("%s type expects gridded data, %s is columnar. "
    295                     "To display columnar data as gridded use the HeatMap "
    296                     "element or aggregate the data (e.g. using rasterize "
    297                     "or np.histogram2d)." %
    298                     (type(self).__name__, self.interface.__name__))

File .../lib/python3.9/site-packages/holoviews/core/data/__init__.py:341, in Dataset.__init__(self, data, kdims, vdims, **kwargs)
    338 kdims, vdims = kwargs.get('kdims'), kwargs.get('vdims')
    340 validate_vdims = kwargs.pop('_validate_vdims', True)
--> 341 initialized = Interface.initialize(type(self), data, kdims, vdims,
    342                                    datatype=kwargs.get('datatype'))
    343 (data, self.interface, dims, extra_kws) = initialized
    344 super(Dataset, self).__init__(data, **dict(kwargs, **dict(dims, **extra_kws)))

File .../lib/python3.9/site-packages/holoviews/core/data/interface.py:256, in Interface.initialize(cls, eltype, data, kdims, vdims, datatype)
    254     continue
    255 try:
--> 256     (data, dims, extra_kws) = interface.init(eltype, data, kdims, vdims)
    257     break
    258 except DataError:

File .../lib/python3.9/site-packages/holoviews/core/data/xarray.py:232, in XArrayInterface.init(cls, eltype, data, kdims, vdims)
    230         undeclared.append(c)
    231     if undeclared and eltype.param.kdims.bounds[1] not in (0, None):
--> 232         raise DataError(
    233             'The coordinates on the %r DataArray do not match the '
    234             'provided key dimensions (kdims). The following coords '
    235             'were left unspecified: %r. If you are requesting a '
    236             'lower dimensional view such as a histogram cast '
    237             'the xarray to a columnar format using the .to_dataframe '
    238             'or .to_dask_dataframe methods before providing it to '
    239             'HoloViews.' % (vdim.name, undeclared))
    240 return data, {'kdims': kdims, 'vdims': vdims}, {}

DataError: The coordinates on the 'R' DataArray do not match the provided key dimensions (kdims). The following coords were left unspecified: ['band']. If you are requesting a lower dimensional view such as a histogram cast the xarray to a columnar format using the .to_dataframe or .to_dask_dataframe methods before providing it to HoloViews.
:DynamicMap   []
keewis commented 2 years ago

thanks for the quick fix, @philippjfr!