Skip to content

AttributeError in GeoPandasInterface.isscalar when a vdim has a custom label and a dim() expression references it by column name #840

Description

@ShaveTheDay

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:

  1. guard against get_dimension returning None and fall back to a string-keyed lookup, or
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions