Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add region interactor, improve selection handling. #571

Merged
merged 12 commits into from
Oct 30, 2024
2 changes: 2 additions & 0 deletions dev/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<option value="driving-shifts">Driving Shifts into Reverse</option>
<option value="earthquakes-feed">Earthquakes Feed</option>
<option value="earthquakes-globe">Earthquakes Globe</option>
<option value="facet-interval">Facet Interval</option>
<option value="flights-200k">Flights 200k</option>
<option value="flights-10m">Flights 10M</option>
<option value="flights-density">Flights Density</option>
Expand All @@ -44,6 +45,7 @@
<option value="population-arrows">Population Arrows</option>
<option value="presidential-opinion">Presidential Opinion</option>
<option value="protein-design">Protein Design</option>
<option value="region-tests">Region Tests</option>
<option value="seattle-temp">Seattle Temperatures</option>
<option value="sorted-bars">Sorted Bars</option>
<option value="splom">Scatter Plot Matrix</option>
Expand Down
20 changes: 17 additions & 3 deletions docs/api/vgplot/interactors.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ Select the nearest value along the y dimension. The supported _options_ are:
- _as_: The [Selection](../core/selection) to populate with filter predicates.
- _field_: The field to select. If not specified, the field backing the `"x"` encoding channel of the most recently added mark is used.

## region

`region(options)`

Select point values from elements within a rectangular region.
Unlike `interval` interactors (which select a domain value range along an axis), the `region` interactor generates clauses for values extracted from the selected set of on-screen elements. This interactor functions similar to `toggle`, but uses a rectangular selection region rather than click / tap interactions.

To select non-visualized data fields, use the plot `channels` property to define additional named channels, which can then be included in this interactor's _channels_ option.

- _as_: The [Selection](../core/selection) to populate with filter predicates. A clause of the form `(field = value1) OR (field = value2) ...` is added for the currently selected values.
- _channels_: An array of encoding channels (e.g., `"x"`, `"y"`, `"color"`) indicating the data values to select. A sub-clause will be included for each channel.
- _peers_: A Boolean-flag (default `true`) indicating if all marks in the current plot should be considered "peers" in the clients set used to perform cross-filtering. A peer mark will be exempt from filtering. Set this to false if you are using a cross-filtered selection but want to filter across marks within the same plot.
- _brush_: An optional object of CSS style attribute-value pairs for the selection brush (SVG `rect`) element.

## interval

Select all values within an interval range.
Expand All @@ -70,7 +84,7 @@ Select a 1D interval range along the x dimension. The supported _options_ are:
- _field_: The field to select. If not specified, the field backing the `"x"` encoding channel of the most recently added mark is used.
- _pixelSize_: The size of an interactive "pixel" (default 1). If set larger, the interval brush will "snap" to a grid larger than visible pixels. In some cases this can be helpful to improve scalability to large data by reducing interactive resolution.
- _peers_: A Boolean-flag (default `true`) indicating if all marks in the current plot should be considered "peers" in the clients set used to perform cross-filtering. A peer mark will be exempt from filtering. Set this to false if you are using a cross-filtered selection but want to filter across marks within the same plot.
- _brush_: An optional object that provides CSS styles for the visible brush.
- _brush_: An optional object of CSS style attribute-value pairs for the selection brush (SVG `rect`) element.

### intervalY

Expand All @@ -82,7 +96,7 @@ Select a 1D interval range along the y dimension. The supported _options_ are:
- _field_: The field to select. If not specified, the field backing the `"y"` encoding channel of the most recently added mark is used.
- _pixelSize_: The size of an interactive "pixel" (default 1). If set larger, the interval brush will "snap" to a grid larger than visible pixels. In some cases this can be helpful to improve scalability to large data by reducing interactive resolution.
- _peers_: A Boolean-flag (default `true`) indicating if all marks in the current plot should be considered "peers" in the clients set used to perform cross-filtering. A peer mark will be exempt from filtering. Set this to false if you are using a cross-filtered selection but want to filter across marks within the same plot.
- _brush_: An optional object that provides CSS styles for the visible brush.
- _brush_: An optional object of CSS style attribute-value pairs for the selection brush (SVG `rect`) element.

### intervalXY

Expand All @@ -95,7 +109,7 @@ Select a 2D interval range along the x and y dimensions. The supported _options_
- _yfield_: The y field to select. If not specified, the field backing the `"y"` encoding channel of the most recently added mark is used.
- _pixelSize_: The size of an interactive "pixel" (default 1). If set larger, the interval brush will "snap" to a grid larger than visible pixels. In some cases this can be helpful to improve scalability to large data by reducing interactive resolution.
- _peers_: A Boolean-flag (default `true`) indicating if all marks in the current plot should be considered "peers" in the clients set used to perform cross-filtering. A peer mark will be exempt from filtering. Set this to false if you are using a cross-filtered selection but want to filter across marks within the same plot.
- _brush_: An optional object that provides CSS styles for the visible brush.
- _brush_: An optional object of CSS style attribute-value pairs for the selection brush (SVG `rect`) element.

## pan & zoom

Expand Down
20 changes: 20 additions & 0 deletions docs/examples/facet-interval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup>
import { coordinator } from '@uwdata/vgplot';
coordinator().clear();
</script>

# Faceted Interval Selections

A faceted plot with 2D interval selections.

<Example spec="/specs/yaml/facet-interval.yaml" />

**Credit**: Adapted from https://observablehq.com/@observablehq/plot-non-faceted-marks

## Specification

::: code-group
<<< @/public/specs/esm/facet-interval.js [JavaScript]
<<< @/public/specs/yaml/facet-interval.yaml [YAML]
<<< @/public/specs/json/facet-interval.json [JSON]
:::
16 changes: 16 additions & 0 deletions docs/examples/region-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup>
import { coordinator } from '@uwdata/vgplot';
coordinator().clear();
</script>

# Region Interactor Tests

<Example spec="/specs/yaml/region-tests.yaml" />

## Specification

::: code-group
<<< @/public/specs/esm/region-tests.js [JavaScript]
<<< @/public/specs/yaml/region-tests.yaml [YAML]
<<< @/public/specs/json/region-tests.json [JSON]
:::
2 changes: 1 addition & 1 deletion docs/examples/wind-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

# Wind Map

`vector` marks on a grid show both direction and intensity—here, the speed of winds. Expressions for `rotate`, `length`, and `stroke` values are evaluated in the database.
`vector` marks on a grid show both direction and intensity—here, the speed of winds. Expressions for `rotate`, `length`, and `stroke` values are evaluated in the database. Both the legend and map support interactive selections to highlight values.

<Example spec="/specs/yaml/wind-map.yaml" />

Expand Down
38 changes: 38 additions & 0 deletions docs/public/specs/esm/facet-interval.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import * as vg from "@uwdata/vgplot";

await vg.coordinator().exec([
vg.loadParquet("penguins", "data/penguins.parquet")
]);

const $sel = vg.Selection.intersect();

export default vg.hconcat(
vg.plot(
vg.frame(),
vg.dot(
vg.from("penguins"),
{x: "bill_length", y: "bill_depth", fill: "#aaa", r: 1}
),
vg.dot(
vg.from("penguins"),
{
x: "bill_length",
y: "bill_depth",
fill: "species",
fx: "sex",
fy: "species"
}
),
vg.intervalXY({as: $sel, brush: {stroke: "transparent"}}),
vg.highlight({by: $sel}),
vg.name("plot"),
vg.grid(true),
vg.marginRight(60),
vg.xDomain(vg.Fixed),
vg.yDomain(vg.Fixed),
vg.fxDomain(vg.Fixed),
vg.fyDomain(vg.Fixed),
vg.fxLabel(null),
vg.fyLabel(null)
)
);
74 changes: 74 additions & 0 deletions docs/public/specs/esm/region-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as vg from "@uwdata/vgplot";

await vg.coordinator().exec([
vg.loadExtension("spatial"),
vg.loadParquet("bls_unemp", "data/bls-metro-unemployment.parquet"),
vg.loadSpatial("feed", "data/usgs-feed.geojson"),
vg.loadSpatial("world", "data/countries-110m.json", {layer: "land"}),
vg.loadSpatial("counties", "data/us-counties-10m.json", {layer: "counties"})
]);

const $series = vg.Selection.single();
const $quakes = vg.Selection.single();
const $counties = vg.Selection.single();

export default vg.vconcat(
vg.plot(
vg.ruleY([0]),
vg.lineY(
vg.from("bls_unemp", {optimize: false}),
{
x: "date",
y: "unemployment",
z: "division",
stroke: "steelblue",
curve: "monotone-x",
mixBlendMode: "multiply"
}
),
vg.region({channels: ["z"], as: $series}),
vg.highlight({by: $series}),
vg.marginLeft(24),
vg.xLabel(null),
vg.xTicks(10),
vg.yLabel("Unemployment (%)"),
vg.yGrid(true),
vg.marginRight(0)
),
vg.vspace(10),
vg.plot(
vg.geo(
vg.from("world"),
{fill: "currentColor", fillOpacity: 0.2}
),
vg.sphere({strokeWidth: 0.5}),
vg.geo(
vg.from("feed"),
{
channels: {id: "id"},
r: vg.sql`POW(10, mag)`,
stroke: "red",
fill: "red",
fillOpacity: 0.2,
title: "title",
href: "url",
target: "_blank"
}
),
vg.region({channels: ["id"], as: $quakes}),
vg.highlight({by: $quakes}),
vg.margin(2),
vg.projectionType("equirectangular")
),
vg.vspace(10),
vg.plot(
vg.geo(
vg.from("counties"),
{channels: {id: "id"}, stroke: "currentColor", strokeWidth: 0.25}
),
vg.region({channels: ["id"], as: $counties}),
vg.highlight({by: $counties}),
vg.margin(0),
vg.projectionType("albers")
)
);
10 changes: 7 additions & 3 deletions docs/public/specs/esm/wind-map.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import * as vg from "@uwdata/vgplot";

await vg.coordinator().exec([
vg.loadParquet("wind", "data/wind.parquet")
vg.loadParquet("wind", "data/wind.parquet", {select: ["*", "row_number() over () as id"]})
]);

const $selected = vg.Selection.union();
const $length = vg.Param.value(2);

export default vg.vconcat(
vg.colorLegend({for: "wind-map", label: "Speed (m/s)"}),
vg.colorLegend({for: "wind-map", label: "Speed (m/s)", as: $selected}),
vg.plot(
vg.vector(
vg.from("wind"),
Expand All @@ -16,9 +17,12 @@ export default vg.vconcat(
y: "latitude",
rotate: vg.sql`degrees(atan2(u, v))`,
length: vg.sql`${$length} * sqrt(u * u + v * v)`,
stroke: vg.sql`sqrt(u * u + v * v)`
stroke: vg.sql`sqrt(u * u + v * v)`,
channels: {id: "id"}
}
),
vg.region({as: $selected, channels: ["id"]}),
vg.highlight({by: $selected}),
vg.name("wind-map"),
vg.lengthScale("identity"),
vg.colorZero(true),
Expand Down
62 changes: 62 additions & 0 deletions docs/public/specs/json/facet-interval.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"meta": {
"title": "Faceted Interval Selections",
"description": "A faceted plot with 2D interval selections.",
"credit": "Adapted from https://observablehq.com/@observablehq/plot-non-faceted-marks"
},
"data": {
"penguins": {
"file": "data/penguins.parquet"
}
},
"hconcat": [
{
"name": "plot",
"plot": [
{
"mark": "frame"
},
{
"mark": "dot",
"data": {
"from": "penguins"
},
"x": "bill_length",
"y": "bill_depth",
"fill": "#aaa",
"r": 1
},
{
"mark": "dot",
"data": {
"from": "penguins"
},
"x": "bill_length",
"y": "bill_depth",
"fill": "species",
"fx": "sex",
"fy": "species"
},
{
"select": "intervalXY",
"as": "$sel",
"brush": {
"stroke": "transparent"
}
},
{
"select": "highlight",
"by": "$sel"
}
],
"grid": true,
"marginRight": 60,
"xDomain": "Fixed",
"yDomain": "Fixed",
"fxDomain": "Fixed",
"fyDomain": "Fixed",
"fxLabel": null,
"fyLabel": null
}
]
}
Loading
Loading