render_points(color="z") silently drops the column and crashes with misleading error
Description
When a points element has a data column named "z" and the user passes color="z", _reparse_points re-registers the element via PointsModel.parse(..., coordinates={"x": "x", "y": "y"}). This silently strips "z" because spatialdata treats it as a reserved spatial coordinate name. The color column vanishes before the color vector is computed, producing KeyError: "Unable to locate color key 'z' for element 'p'." — even though the column is plainly visible in the original element's .columns. The error message gives no indication that an internal re-registration step dropped the column.
Environment
spatialdata-plot: 0.3.4.dev (main, 5cfedc7)
spatialdata: 0.5.0
Python: 3.13
Minimal Reproducible Example
import matplotlib; matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd
import dask; dask.config.set({"dataframe.query-planning": False})
import spatialdata as sd
from spatialdata.models import PointsModel
import spatialdata_plot
# Points with a data column named "z" (not a 3-D spatial z — just a data column)
pts = PointsModel.parse(
pd.DataFrame({"x": [1., 2., 3.], "y": [1., 2., 3.], "z": [0.1, 0.5, 0.9]}),
coordinates={"x": "x", "y": "y"},
)
print("Column 'z' exists:", "z" in pts.columns) # True
sdata = sd.SpatialData(points={"p": pts})
fig, ax = plt.subplots()
sdata.pl.render_points("p", color="z").pl.show(ax=ax)
# KeyError: "Unable to locate color key 'z' for element 'p'."
Expected vs. Actual
Expected: Renders points colored by the "z" data column. Or, if this cannot be supported, raises a clear and actionable error:
"Column 'z' was dropped during internal re-registration because it conflicts with the reserved spatial coordinate name 'z'. Rename the column before plotting."
Actual: KeyError: "Unable to locate color key 'z' for element 'p'. Please ensure the key exists in a table annotating this element." — misleading because "z" is visible in pts.columns.
Fix Sketch
In _reparse_points (render.py:~808), after PointsModel.parse, compare the original DataFrame's columns to the result. If any columns are missing, emit a clear warning naming the dropped columns and the reason (reserved coordinate name conflict).
A more robust fix: before calling PointsModel.parse, detect columns whose names conflict with reserved spatial coordinate names ("x", "y", "z") and temporarily rename them (e.g., "z" → "__z_data__"), then rename back after parsing. Alternatively, skip _reparse_points entirely when no spatial filtering has actually occurred, which avoids the re-registration side effect altogether.
Labels: bug, points, priority: medium
Triage tier: Tier 2
render_points(color="z")silently drops the column and crashes with misleading errorDescription
When a points element has a data column named
"z"and the user passescolor="z",_reparse_pointsre-registers the element viaPointsModel.parse(..., coordinates={"x": "x", "y": "y"}). This silently strips"z"because spatialdata treats it as a reserved spatial coordinate name. The color column vanishes before the color vector is computed, producingKeyError: "Unable to locate color key 'z' for element 'p'."— even though the column is plainly visible in the original element's.columns. The error message gives no indication that an internal re-registration step dropped the column.Environment
Minimal Reproducible Example
Expected vs. Actual
Expected: Renders points colored by the
"z"data column. Or, if this cannot be supported, raises a clear and actionable error:Actual:
KeyError: "Unable to locate color key 'z' for element 'p'. Please ensure the key exists in a table annotating this element."— misleading because"z"is visible inpts.columns.Fix Sketch
In
_reparse_points(render.py:~808), afterPointsModel.parse, compare the original DataFrame's columns to the result. If any columns are missing, emit a clear warning naming the dropped columns and the reason (reserved coordinate name conflict).A more robust fix: before calling
PointsModel.parse, detect columns whose names conflict with reserved spatial coordinate names ("x","y","z") and temporarily rename them (e.g.,"z"→"__z_data__"), then rename back after parsing. Alternatively, skip_reparse_pointsentirely when no spatial filtering has actually occurred, which avoids the re-registration side effect altogether.Labels: bug, points, priority: medium
Triage tier: Tier 2