ScatterPlot
Scatterplots are a LineChart alternative that allows for disconnected data points with configurable size and colors.
Charts rely on a DataSource
to provide their data, and use a series of accessors to optionally control formatting. It is recommended to read the DataSource
documentation first.
Usage
One common use of a ScatterPlot
is to draw probabilistic sensor data, multidimensional signals, or signals which don't suit a 'line based' point-to-point relationship.
To use a ScatterPlot
, the minimum requirements are similar to other chart types:
ChartContainer
is required to provide a parent context,- A
MessageDataSource
orDataSource
to provide an inboundEvent
stream, - A domain,
RealTimeDomain
orRealTimeSlicingDomain
.
import { ChartContainer , ScatterPlot , RealTimeSlicingDomain , HorizontalAxis , VerticalAxis } from '@electricui/components-desktop-charts'import { useMessageDataSource } from '@electricui/core-timeseries' const OverviewPage = () => { // structured const sensorDataSource = useMessageDataSource ('propellor') return ( <React .Fragment > <ChartContainer > <ScatterPlot dataSource ={sensorDataSource } /> <RealTimeSlicingDomain window ={10000} /> <VerticalAxis /> <HorizontalAxis /> </ChartContainer > </React .Fragment > )}
However, as ScatterPlot
are often used for multidimensional, or non-timeseries data, some custom event processing will usually be required (or inbound data must be pre-structured) to provide suitable values for plotting.
Data Preparation
- Use a Transformer pipeline to format the
Event
stream, or - Use accessor syntax with
accessor
, and optionallycolorAccessor
andsizeAccessor
, or - Provide a formatted
DataSource
with structured fields:- Positioning of points with
x
andy
andz
members, - Color control by providing
r
,g
andb
membersTHREE.Color
orstring
, - Size control with the
size
member.
- Positioning of points with
Ultimately, positioning of the dots is achieved with
x
,y
,z
members in theEvent
.Optionally format each point with
size
,r
,g
,b
members.
const scatterEvent = {x: data.servoA,y: data.servoB,z: time,size: data.systemLoad,r: 0.06, // Color channels are [0-1] floating point valuesg: 153 / 255,b: 96 / 255,}
Axis and Domains
Use TimeAxis
with RealTimeDomain
for conventional one-dimensional data streamed from hardware.
The simple numeric datasource is automatically handled.
import { ChartContainer , ScatterPlot , RealTimeDomain , TimeAxis , VerticalAxis } from '@electricui/components-desktop-charts'import { useMessageDataSource } from '@electricui/core-timeseries' const OverviewPage = () => { const analogDataSource = useMessageDataSource ('sine') return ( <React .Fragment > <ChartContainer > <ScatterPlot dataSource ={analogDataSource } /> <RealTimeDomain window ={10000} /> <VerticalAxis /> <TimeAxis /> </ChartContainer > </React .Fragment > )}
For multi-dimensional data, use HorizontalAxis
and RealTimeSlicingDomain
.
import { ChartContainer , ScatterPlot , RealTimeSlicingDomain , VerticalAxis , HorizontalAxis } from '@electricui/components-desktop-charts'import { useMessageDataSource } from '@electricui/core-timeseries'import { coalesce } from '@electricui/dataflow'import { useDataTransformer } from '@electricui/timeseries-react'import { Colors } from '@blueprintjs/core' const OverviewPage = () => { const xDataSource = useMessageDataSource ('lvdt_x') const yDataSource = useMessageDataSource ('lvdt_y') const manipulatedDataSource = useDataTransformer (() => { return coalesce ({x : xDataSource , y : yDataSource }) }) return ( <React .Fragment > <ChartContainer width ={400} height ={400}> <ScatterPlot dataSource ={manipulatedDataSource } color ={Colors .BLUE5 } size ={4} /> <RealTimeSlicingDomain window ={5000} xMin ={0} xMax ={1000} yMin ={0} yMax ={1000} /> <VerticalAxis /> <HorizontalAxis /> </ChartContainer > </React .Fragment > )}
See the coalesce
transformer documentation for notes on how DataSource
pairs are being remapped to the x
and y
members required by ScatterPlot
.
Geometry Cache size
The ScatterPlot
component uses a statically declared internal buffer for geometry storage.
By default, this is internally managed, but can be overridden with the maxItems
property to increase/reduce the potential RAM impact of the chart.
This is helpful when you know the incoming data stream is slow, only covers a short timespan, or if high bandwith data is being plotted (>10kps, >1M points).
Electric UI will internally allocate the internal ping-pong geometry buffers as deemed optimal for performance.
As such, the actual value may be some multiple of
4096
entries.
<ScatterPlot dataSource ={analogDataSource } maxItems ={6000} />
If
maxItems
isn't big enough to buffer for the selected domain, then users will see data dropping off the scatterplot.
Formatting
Global Color
For global colour configuration, the color
property accepts a string or number.
Passing hex color="#0066cc
locally is fine, but we recommend using centralised colors to maintain consistency.
import { Colors } from '@blueprintjs/core'
<ScatterPlot dataSource ={sensorDS } color ="#0066cc" /><ScatterPlot dataSource ={sensorDS } color ={Colors .GREEN5 } />
It can also accept a threejs Color
.
import { Color } from 'three' const OverviewPage = () => { const col = new Color () col .setHSL (0.1, 0.7, 0.6) return ( <React .Fragment > <ChartContainer > <ScatterPlot dataSource ={sensorDS } color ={col } /> <RealTimeDomain window ={10000} /> <VerticalAxis /> <TimeAxis /> </ChartContainer > </React .Fragment > )}
Color Accessor
For per-point color control, use accessor syntax to return a string, hex value or threejs Color
.
This can be used to highlight values of interest, or integrate another dimension of sensor data into the scatter point properties.
const OverviewPage = () => { function colorFromValue (value : number): string { if (value > WARN_HIGH ) { return Colors .RED3 } else if (value < WARN_LOW ) { return Colors .COBALT3 } else { return Colors .GREEN5 } } return ( <React .Fragment > <ChartContainer > <ScatterPlot dataSource ={sensorDS } colorAccessor ={(data , time ) => colorFromValue (data .y )} /> <RealTimeDomain window ={10000} /> <VerticalAxis /> <TimeAxis /> </ChartContainer > </React .Fragment > )}
For a more complex example which interpolates color values, look at this example:
Global Size
Rendered point size is globally configured by providing the size
property with a positive number.
<ScatterPlot dataSource ={sensorDS } size ={1} /><ScatterPlot dataSource ={sensorDS } size ={4} /><ScatterPlot dataSource ={sensorDS } size ={8} />
Size Accessor
For per-point size control, use accessor syntax to return a number.
An example use-case is conveying the signal-strength or confidence values of rangefinder sensors through variable sizing.
Carefully consider the legibility of your resulting plot with variable sizing, and consider clamping the minimum and maximum point size to legible values
This example interpolates point sizing in a similar manner to the hue interpolation described in the Scatterplot Gradient Example.
const OverviewPage = () => { function valueToSize (value : number): number { // Normalise the raw value into 0-1 based on min/max const valueInRange = invlerp (SENSITIVITY_MIN , SENSITIVITY_MAX , value ) // Re-map the 0-1 to a clamped size const size = lerp (PT_SIZE_MIN , PT_SIZE_MAX , valueInRange ) return size } return ( <ChartContainer > <ScatterPlot dataSource ={lidarDS } color ={Colors .BLUE5 } sizeAccessor ={(data , time ) => valueToSize (data .strength ) } /> <RealTimeDomain window ={10000} /> <VerticalAxis /> <TimeAxis /> </ChartContainer > )}