Skip to content

bqplot backend: "diamond", "plus" and "crosshair" markers silently render as nothing; marker size ignored #208

Description

@mwcraig

Description

In the bqplot backend, several marker shapes that astro-image-display-api documents as supported are silently not drawn at all, and the marker size in catalog_style is ignored.

Versions: astrowidgets 0.5.1, bqplot 0.12.45, bqplot-image-gl 1.8.0, astro-image-display-api (current release).

1. diamond, plus and crosshair markers render as nothing, with no warning

The AIDA set_catalog_style docs say:

The following shapes are supported: "circle", "square", "crosshair", "plus", "diamond".

plot_named_markers in astrowidgets/bqplot.py passes the shape name straight to a bqplot ScatterGL mark. The Python-side marker trait accepts all 12 bqplot marker names, but the ScatterGL WebGL frontend only implements 6 of them. From the fragment shader in the bqplot labextension bundle:

#define FAST_CIRCLE 1
#define FAST_SQUARE 2
#define FAST_ARROW 3
#define FAST_CROSS 4
#define FAST_TRIANGLE_UP 5
#define FAST_TRIANGLE_DOWN 6

For any other shape name, FAST_DRAW is never defined, the shader's inner_shape/outer_shape stay 0.0, and the marker is drawn fully transparent. No error or warning is raised anywhere — the marker just doesn't appear. Three of the five AIDA-documented shapes (diamond, plus, crosshair) fall in this hole.

Reproducer:

import numpy as np
from astropy.table import Table
from astrowidgets.bqplot import ImageWidget

iw = ImageWidget()
iw.load_image(np.random.normal(size=(100, 100)))
iw.load_catalog(
    Table({"x": [30.0], "y": [40.0]}),
    catalog_label="cat",
    catalog_style={"shape": "diamond", "color": "red", "size": 20},
)
# Nothing appears. With "square" instead of "diamond" a marker appears.

We hit this in stellarphot (stellarphot#584): APASS comparison stars styled with "diamond" and exclusion markers styled with "plus" were invisible, while "square" markers in the same viewer rendered fine.

Recommended fix (edited 2026-07-09 after field-testing; see the follow-up comment below for details): construct a plain SVG Scatter instead of ScatterGL in plot_named_markers.

  • ScatterGL subclasses Scatter and overrides only _view_name/_model_name (bqplot 0.12.45), so it is trait-for-trait identical — a one-word change at the single construction site.
  • The SVG frontend draws all 12 marker shapes, fixing this bug outright.
  • Field-tested in stellarphot via the monkeypatch astrowidgets.bqplot.ScatterGL = bqplot.Scatter: diamonds and plusses render correctly, SVG marks compose fine over the ImageGL layer, and pan/zoom stays smooth at realistic catalog sizes.
  • In bqplot 0.13 the GL marks move out to the separate bqplot-gl package — that's what broke the import in Cannot import name 'ScatterGL' from 'bqplot' (bqplot==0.13.0rc0) #196, closed with an upper pin. Staying on ScatterGL under 0.13 would mean a new dependency and an import change, and would buy nothing for this bug: bqplot-gl's scatter-fragment.glsl has the identical six-shape FAST_* list. Switching to Scatter lets the bqplot<0.13 pin be lifted with no new dependency.

The trade-off is rendering performance for very large catalogs (SVG creates a DOM node per point and slows down somewhere in the thousands of points). If that matters, an opt-in GL path could be kept, translating the shape names the shader can't draw (e.g. plus/crosshair → GL cross, which renders as a plus sign) and warning when a shape can't be honored.

2. Marker size is ignored

plot_named_markers hardcodes default_size=100 and never uses its size argument:

sc = ScatterGL(scales=scale_dict,
               x=x, y=y,
               colors=[color],
               default_size=100,
               marker=shape,
               fill=False)

so the size in catalog_style (passed as size=size**2 from set_catalog_style) has no effect. The fix is to pass default_size=size — the value arriving from set_catalog_style is already squared, i.e. already in the px² units default_size expects.

3. (minor) Confusing traittypes warning on every catalog load

plot_named_markers passes the catalog table's Column objects to the ScatterGL x/y traits. np.asarray copies an ndarray subclass even when the dtype matches, so traittypes emits the baffling

UserWarning: Given trait value dtype "float64" does not match required type "float64". A coerced copy has been created.

on every catalog load (np.dtype(None).name is "float64", hence the self-contradictory message). Calling np.asarray(...) on the coordinates before handing them to the mark would avoid it.


This issue was researched and written by Claude (Anthropic's AI assistant) at the direction of @mwcraig, who reviewed it before posting. Edited 2026-07-09, also by Claude at @mwcraig's direction, to replace the list of candidate fixes with the field-tested recommendation.

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