AttributeError in GeoPandasInterface.isscalar when a vdim has a custom label and a dim() expression references it by column name
Software Version Info
gv.__version__='1.15.1'
hv.__version__='1.22.1'
bokeh.__version__='3.9.0'
Description of expected behavior and the observed behavior
When a gv element (e.g. gv.Points) is constructed from a GeoDataFrame with a value dimension that has a custom label — i.e. a (name, label) tuple or an hv.Dimension(name, label=...) where label != name — and a dim('<column_name>') expression is then used in an option (e.g. color=dim('status').categorize(...)), rendering raises:
AttributeError: 'NoneType' object has no attribute 'name'
raised in geoviews/data/geopandas.py inside GeoPandasInterface.isscalar.
Expected behavior: rendering succeeds and the dim() expression resolves against the column name, regardless of whether the dimension's label differs from its name. This is the behavior with the plain-pandas PandasInterface for an identical hv.Points setup, so the geopandas interface should match.
Observed behavior: AttributeError during range computation. The same code with a plain-string vdim (vdims=['status']) works correctly, which is what makes this surprising — adding a human-readable label to a column should not change whether the plot renders.
Root cause (brief)
In GeoPandasInterface.isscalar (geoviews/data/geopandas.py, ~line 265):
@classmethod
def isscalar(cls, dataset, dim, per_geom=False):
dim = dataset.get_dimension(dim) # returns None when label != name
...
elif per_geom:
return all(isscalar(v) or len(list(unique_array(v))) == 1
for v in dataset.data[dim.name]) # AttributeError here
get_dimension is called with a Dimension object (built from the dim() expression, with label == column_name). Dimension.__eq__ compares labels only (holoviews/core/dimension.py), so it fails to match the element's stored Dimension, whose label is the user-supplied custom label. get_dimension returns None, and the next line dereferences .name on it.
The string-keyed lookup path in get_dimension uses a name_map that checks both name and label, so passing a string would have matched. The Dimension-to-Dimension comparison path is the one that breaks.
Complete, minimal, self-contained example code that reproduces the issue
import geopandas as gpd
import holoviews as hv
import geoviews as gv
from holoviews import dim
from shapely.geometry import Point
import bokeh
hv.extension('bokeh')
gv.extension('bokeh')
print(f'{gv.__version__=}\n{hv.__version__=}\n{bokeh.__version__=}')
gdf = gpd.GeoDataFrame(
{
'status': ['safe', 'endangered', 'safe'],
'geometry': [Point(0, 0), Point(1, 1), Point(2, 2)],
},
crs='EPSG:4326',
)
# vdim with a custom label (label != name)
points = gv.Points(gdf, vdims=[('status', 'Conservation Status')])
# dim() expression references the column by NAME
color_dim = dim('status').categorize({'safe': 'green', 'endangered': 'red'})
overlay = points.opts(gv.opts.Points(color=color_dim, size=10))
hv.render(overlay) # AttributeError: 'NoneType' object has no attribute 'name'
The same failure occurs if the vdim is given as hv.Dimension('status', label='Conservation Status') instead of the tuple form.
The error does not occur if:
- the vdim is a plain string (
vdims=['status']), or
- the same code is run with a plain-pandas-backed
hv.Points (no GeoPandas involved) — confirming the bug is specific to GeoPandasInterface.
Traceback
File ".../holoviews/plotting/plot.py", line 761, in _compute_group_range
values = v.apply(el, all_values=True)
File ".../holoviews/util/transform.py", line 753, in apply
(dataset.interface.multi and dataset.interface.isunique(dataset, dimension, True)))
File ".../holoviews/core/data/interface.py", line 316, in isunique
return cls.isscalar(dataset, dim, per_geom)
File ".../geoviews/data/geopandas.py", line 271, in isscalar
for v in dataset.data[dim.name])
AttributeError: 'NoneType' object has no attribute 'name'
Possible fix
GeoPandasInterface.isscalar (and likely a few sibling methods in the same file that follow the dim = dataset.get_dimension(dim); ...dataset.data[dim.name]... pattern) should either:
- guard against
get_dimension returning None and fall back to a string-keyed lookup, or
- resolve the dimension by name/string before calling
get_dimension, since the string path is what handles the name-vs-label mismatch correctly.
A grep for get_dimension followed by .name access in geoviews/data/geopandas.py should surface every site that needs the same treatment.
Other
For transparency, the above error surfaced while developing a convenience wrapper for portions of the geoviews API, with the assistance of Claude Code. Claude has written this issue ticket and implemented it's suggested fix in the wrapper.
AttributeErrorinGeoPandasInterface.isscalarwhen a vdim has a custom label and adim()expression references it by column nameSoftware Version Info
Description of expected behavior and the observed behavior
When a
gvelement (e.g.gv.Points) is constructed from aGeoDataFramewith a value dimension that has a custom label — i.e. a(name, label)tuple or anhv.Dimension(name, label=...)wherelabel != name— and adim('<column_name>')expression is then used in an option (e.g.color=dim('status').categorize(...)), rendering raises:raised in
geoviews/data/geopandas.pyinsideGeoPandasInterface.isscalar.Expected behavior: rendering succeeds and the
dim()expression resolves against the column name, regardless of whether the dimension's label differs from its name. This is the behavior with the plain-pandasPandasInterfacefor an identicalhv.Pointssetup, so the geopandas interface should match.Observed behavior:
AttributeErrorduring range computation. The same code with a plain-string vdim (vdims=['status']) works correctly, which is what makes this surprising — adding a human-readable label to a column should not change whether the plot renders.Root cause (brief)
In
GeoPandasInterface.isscalar(geoviews/data/geopandas.py, ~line 265):get_dimensionis called with aDimensionobject (built from thedim()expression, withlabel == column_name).Dimension.__eq__compares labels only (holoviews/core/dimension.py), so it fails to match the element's storedDimension, whose label is the user-supplied custom label.get_dimensionreturnsNone, and the next line dereferences.nameon it.The string-keyed lookup path in
get_dimensionuses aname_mapthat checks both name and label, so passing a string would have matched. The Dimension-to-Dimension comparison path is the one that breaks.Complete, minimal, self-contained example code that reproduces the issue
The same failure occurs if the vdim is given as
hv.Dimension('status', label='Conservation Status')instead of the tuple form.The error does not occur if:
vdims=['status']), orhv.Points(no GeoPandas involved) — confirming the bug is specific toGeoPandasInterface.Traceback
Possible fix
GeoPandasInterface.isscalar(and likely a few sibling methods in the same file that follow thedim = dataset.get_dimension(dim); ...dataset.data[dim.name]...pattern) should either:get_dimensionreturningNoneand fall back to a string-keyed lookup, orget_dimension, since the string path is what handles the name-vs-label mismatch correctly.A grep for
get_dimensionfollowed by.nameaccess ingeoviews/data/geopandas.pyshould surface every site that needs the same treatment.Other
For transparency, the above error surfaced while developing a convenience wrapper for portions of the geoviews API, with the assistance of Claude Code. Claude has written this issue ticket and implemented it's suggested fix in the wrapper.