vega / vl-convert

Utilities for converting Vega-Lite specs from the command line and Python
BSD 3-Clause "New" or "Revised" License
84 stars 9 forks source link

predefined values not evaluated upon saving #136

Closed mattijn closed 7 months ago

mattijn commented 7 months ago

I noticed that colors as defined by a condition based on a selection value not is evaluated (ref: https://github.com/altair-viz/altair/pull/3275).

I would expect something as such: image

But it is saved as such: image

Probably something with this part

# defintion for interactive brush
brush = alt.selection_interval(
    encodings=['longitude'],
    empty=False,
    value={"longitude": [-50, -110]}
)

Full reproducible example:

import altair as alt
from vega_datasets import data
import geopandas as gpd

# load data
gdf_quakies = gpd.read_file(data.earthquakes.url, driver="GeoJSON")
gdf_world = gpd.read_file(data.world_110m.url, driver="TopoJSON")

# defintion for interactive brush
brush = alt.selection_interval(
    encodings=['longitude'],
    empty=False,
    value={"longitude": [-50, -110]}
)

# world disk
sphere = alt.Chart(alt.sphere()).mark_geoshape(
    fill="transparent", stroke="lightgray", strokeWidth=1
)

# countries as shapes
world = alt.Chart(gdf_world).mark_geoshape(
    fill="lightgray", stroke="white", strokeWidth=0.1
)

# earthquakes as dots on map
quakes = alt.Chart(gdf_quakies).transform_calculate(
    lon="datum.geometry.coordinates[0]",
    lat="datum.geometry.coordinates[1]",
).mark_circle(opacity=0.35, tooltip=True).encode(
    longitude="lon:Q",
    latitude="lat:Q",
    color=alt.condition(brush, alt.value('goldenrod'), alt.value('steelblue')),
    size=alt.Size("mag:Q").scale(type="pow", range=[1, 1000], domain=[0, 7], exponent=4),
).add_params(brush)

# combine layers for the map
left_map = alt.layer(sphere, world, quakes).project(type="mercator")

# histogram of binned earthquakes
bars = alt.Chart(gdf_quakies).mark_bar().encode(
    x=alt.X('mag:Q').bin(extent=[0,7]),
    y='count(mag):Q',
    color=alt.value('steelblue')
)

# filtered earthquakes
bars_overlay = bars.encode(color=alt.value('goldenrod')).transform_filter(brush)

# combine layers for histogram
right_bars = alt.layer(bars, bars_overlay)

# vertical concatenate map and bars
left_map | right_bars
jonmmease commented 7 months ago

Thanks for the report. Looking at the Vega spec that Vega-Lite generates here I noticed something. Selections in Vega-Lite are represented as Vega datasets with a _store suffix. The selection that represents the interval selection is stored as the param_3_store dataset. In the Vega editor Data Viewer, the initial value of `param_3store is:

Screenshot 2023-11-26 at 6 21 59 AM

But the initial value specified in the dataset itself in the spec is:

    {
      "name": "param_3_store",
      "transform": [{"type": "collect", "sort": {"field": "_vgsid_"}}],
      "values": [{"unit": "view_3", "_vgsid_": [-50, -110]}]
    },

It looks like the param_3_store dataset is initialized with the range of the selection rather with the IDs of the selected data points, so the first time the selection is evaluated nothing looks selected.

The way this works normally is that the the store is updated in response to the geo_interval_init_tick signal, which triggers every millisecond.

    {
      "name": "geo_interval_init_tick",
      "value": null,
      "on": [
        {
          "events": "timer{1}",
          "update": "geo_interval_init_tick === null ? {} : geo_interval_init_tick"
        }
      ]
    },

Looks like this boils down to https://github.com/vega/vega/issues/3481.

I'll see if I can make a corresponding workaround in vl-convert

jonmmease commented 7 months ago

I got this worked out in https://github.com/vega/vl-convert/pull/138