Skip to main content

Advanced visualization

This page uses Wasm code blocks so you can run the examples directly in the browser.

SA Engine provides a range of more advanced visualization types, such as Multi plot and Geo JSON.

Audio​

Audio will visualize a stream of vectors as a bar plot and a wave form where the vector indices correspond to specific frequencies and the values are the intensity. If possible, the sound will play the in your speakers/headphones. You can regulate the buffer size with the slider bar and sampling rate in the drop down menu.

Warning

Ensure that your speaker/headphone volume is turned down before trying the audio examples. This is to prevent any damage to the speakers or your hearing. Stream Analyze is not responsible for any material or physical damage incurred from running the examples below.

A siren-like sound can be generated by using the functions from the simulated audio chapter in the tutorial.

//plot: Multi plot
{"sa_plot": "Text"};
create function get_frequency(Number low, Number high, Number period, Number t) -> Number
as low + ((high - low)/2.0) * (1 + sin(2 * 3.1415926 * t / period));

create function get_phase(Number low, Number high, Number period, Number t) -> Number
as low * t + ((high - low)/2.0) * (t - cos(2 * 3.1415926 * t / period));

create function simaudio(Number windowsize, Number samplerate)
-> Stream of Vector of Number
/* A simulated audio stream that varies in frequency
between 100-900 Hz every 10 seconds. */
as winagg(
(select Stream of y
from Number t, Number y, Number phase
where t in heartbeat(1/samplerate)
and phase = get_phase(100.0, 900.0, 10.0, t)
and y = sin(2 * 3.1415926 * phase)),
windowsize, windowsize);
{"sa_plot": "Audio"};
simaudio(256, 16000);

Parallel coordinates​

Regular​

Parallel coordinates (p-coord) is a very good alternative when visualizing many dimensions.

Note

Parallel coordinates are a common way of visualizing high-dimensional geometry and analyzing multivariate data. To show a set of points in an n-dimensional space, a backdrop is drawn consisting of n parallel lines, typically vertical and equally spaced. A point in n-dimensional space is represented as a polyline with vertices on the parallel axes; the position of the vertex on the i:th axis corresponds to the i:th coordinate of the point.

//plot: p-coords
select { "cos(x)": cos(x),
"sin(x)": sin(x),
"mod(x,2)": mod(round(x),2),
"cos(x)*sin(x)": cos(x)*sin(x)
}
from Number x
where x in heartbeat(0.01)*100
limit 500;
Note

You can drag on each axis label to reorder the dimensions. Click and drag on an axis to only view selected lines. Click on part of the axis that isn't selected to remove selection.

Plotting batches​

Batch data types are simply plotted all at once.

//plot: p-coords
select vector of { "cos(x)": cos(x),
"sin(x)": sin(x),
"mod(x,2)": mod(round(x),2),
"cos(x)*sin(x)": cos(x)*sin(x)
}
from Number x
where x in heartbeat(0.01)*100
limit 100

Multi plot​

Multi plot is a powerful visualization type that can be useful in lot's of different scenarios. It uses a JSON record that describes how to visualize the data.

Using Multi plot for single plots​

Multi plot can be used to plot any of the previously mentioned visualization types. Why use Multi plot when you can just use the regular //plot: <type> for single plots you might ask? Well, with Multi plot you can specify parameters that you cannot to with the regular plots. For example, in the Scatter plot description we mentioned that "default settings for Scatter plot is to use the first label in the record as X-axis, the second label in the record as Y-axis, the third label in the record as size on the points, and the color is turned off" and "To colorize the points you have to click the settings icon (cogwheel) in the plot". But with Multi plot you can create Scatter plots and specify the parameters for size and color directly in the JSON record.

The JSON record for plotting single plots with Multi plot has the following format:

{
"sa_plot": <value>,
"<visualization-param-1>": <value>,
"<visualization-param-2>": <value>,
...
}

Where the value for sa_plot is the visualization type (e.g., "Line plot" or "Scatter plot"), and the visualization parameters can be things like "size_axis" or "color_axis". The visualization parameters are settings for the specific visualization type.

Example

An example for plotting two-dimensional vectors in a Scatter plot:

{
"sa_plot": "Scatter plot",
"size_axis": "none",
"color_axis": 2
};

The record says that we want a Scatter plot, all points should have the same size, and the color should be determined by the Y-values (i.e., the second element in each vector).

The following table lists some common visualization parameters:

Visualization paramValueDescription
batch<1|0>if 1 save data until end of stream. Otherwise stream as normal.
labels<vector>Which labels to display.
memory<number>When not using batch, memory determines how many points to be visualized in plot.
size_axis<label|number|"none">Which label to use as size of elements in a Scatter plot. A number indicates an index in a vector.
color_axis<label|number|"none">Which label to use as color of elements in a Scatter plot. A number indicates an index in a vector.
grid{"w": <value>, "h": value [, "x": value] }Size and position of plot in the plotting pane

Examples​

Example 1: Labels and memory​

Here we visualize multiple series with a line plot. We specify each series with a label and tell the plot to keep 100 values in memory.

//plot: Multi plot
{
"sa_plot": "Line plot",
"labels":["cos","sin","mod","cos*sin"],
"memory": 100
};
select [cos(x), sin(x), mod(round(x),2), cos(x)*sin(x)]
from Number x
where x in heartbeat(0.01)*100
limit 200
Example 2: Size axis and color axis​

Here we visualize the scatter plot from the Scatter plot description but instead of using the settings dialog to colorize the points we use the Multi plot JSON record to set the color axis.

//plot: Multi plot
{
"sa_plot": "Scatter plot",
"size_axis": "none",
"color_axis": "cos(x)*sin(x)"
};
select { "cos(x)": cos(x),
"sin(x)": sin(x),
"mod(x,2)": mod(round(x),2),
"cos(x)*sin(x)": cos(x)*sin(x)
}
from Number x
where x in heartbeat(0.01)*100
limit 500;

We can get the same effect without labels if the query returns a vector and we use the vector index to specify color axis.

//plot: Multi plot
{
"sa_plot": "Scatter plot",
"size_axis": "none",
"color_axis": 4
};
select [cos(x), sin(x), mod(round(x),2), cos(x)*sin(x)]
from Number x
where x in heartbeat(0.01)*100
limit 500;
Example 3: Colorizing classified points​

Here we want to plot classified points in 2D space [x, y, class] and use the same color for all points belonging to the same class. We do this by creating a JSON record in the query, with labels for each vector index. We then use the "class" label in the "color_axis" attribute. Note that we could just as well have used the original vector index and not create a JSON record with labels as query result, but adding labels makes it much clearer that the third element in the vector is the classification.

//plot: Multi plot
create function gen_datapoint(Number rad, Number i)
-> Vector of Number
as [mod(i,3)*2 + frand(rad) * sin(i*2*pi()/1000),
0 + frand(rad) * cos(i*2*pi()/1000), mod(i,3)];

{
"sa_plot": "Scatter plot",
"size_axis": "none",
"color_axis": "class"
};
select vector of { "X": v[1], "Y": v[2], "class": v[3] }
from Vector v
where v in gen_datapoint(1.0, range(5000));

An even simpler syntax that accomplishes the same thing is to use the labels attribute. If we use the labels attribute, then we don't have to create a JSON record as a result from the query. Note however that in the preceding example we created a batch result Vector of Record, but here the query result is a vector so we need to either increase the plot memory with the "memory" attribute, or set the "batch" attribute to 1 to see all points. Simplest is to set the "batch" attribute to 1 since it does not require us to know how many points we are going to plot.

//plot: Multi plot
create function gen_datapoint(Number rad, Number i)
-> Vector of Number
as [mod(i,3)*2 + frand(rad) * sin(i*2*pi()/1000),
0 + frand(rad) * cos(i*2*pi()/1000), mod(i,3)];

{
"sa_plot": "Scatter plot",
"labels": ["X","Y","class"],
"size_axis": "none",
"color_axis": "class",
"batch": 1
};
select v
from Vector v
where v in gen_datapoint(1.0, range(5000));

Multiple plots​

Visualizing single plots and being able to specify parameters is all well and good, but it is when visualizing multiple plots that Multi plot really comes to shine.

The JSON record for visualizing multiple plots with Multi plot has the following format (note that the attribute is now "sa_plots" (plural) instead of "sa_plot" as it was in the case for single plots):

{
"sa_plots": [<string>||<sa_plot>],
"<visualization-param-1>": <value>,
"<visualization-param-2>": <value>,
...
}

The "sa_plots" attribute is a list of plots we want to use (e.g., ["Line plot", "Scatter plot"]). Each value in the list can either be a string (e.g., "Line plot") or a complete "sa_plot" record object (e.g., {"sa_plot": "Scatter plot", "memory": 500}).

By default all series in the query result will be sent to all plots, but we will cover how to send different series of a query to separate plots later on.

Also note that attributes outside the "sa_plots" list are applied to all plots in the list.

Example

An example for plotting two-dimensional vectors in three separate plots:

{
"sa_plots": [
{"sa_plot": "Line plot" },
{"sa_plot": "Scatter plot", "memory": 500},
"Bar plot"
],
"labels": ["x","y"]
};

The record says that we want three plots: a Line plot, a Scatter plot, and a Bar plot. The Scatter plot should remember 500 points. And we set the labels to "x" and "y" for all three plots.

Plotting all outputs in all plots​

The default for Mulitplot is to plot all outputs in all plots.

Example: Plotting a vector in four separate plots​

Here we produce a four-dimensional vector and plot the vector in four separate plots. Note how we set labels and memory for the Line plot, batch for the Bar plot, but use the default parameter values for the other plots.

//plot: Multi plot
{
"sa_plots": [{ "sa_plot": "Line plot",
"labels":["simsig","sin","cos","sin/cos"],
"memory": 200
},
{ "sa_plot": "Bar plot",
"batch": 1
},
"Scatter plot",
"p-coords",
"Pie chart"]
};
select [simsig(x), sin(x), cos(x), (sin(x)+1.1)/(cos(x)+1.1)]
from Number x
where x in iota(1,200)/10
order by x;
Pie chart plot

Note that the pie chart only plots the result for the last vector x=20 while the other plots plot the result for all 200 vectors.

Plotting different parts of output in separate plots​

You can redirect different parts of the output to separate plots. For example, if you have a two-dimensional vector as output from your query, you can plot the first element of each vector in one plot and the second element of each vector in another plot. You do this by assigning labels to the output and then specify which label(s) to plot in each "sa_plot" record.

For example, the following record shows how to plot the X-value of a two-dimensional vector in one plot and the Y-value in another plot:

{
"sa_plots": [
{"sa_plot": "Line plot", "labels": ["x"]},
{"sa_plot": "Line plot", "labels": ["y"]}
],
"labels": ["x","y"]
};
Example: Plotting elements of two-dimensional vector in separate plots​

Here we plot the X-value of a two-dimensional vector in one Line plot and the Y-value in another Line plot. As a bonus we position the two plots side-by-side with the "grid" attribute.

//plot: Multi plot
{
"sa_plots": [
{"sa_plot": "Line plot", "labels": ["x"], "grid": {"w": 50, "h":15}},
{"sa_plot": "Line plot", "labels": ["y"], "grid": {"w": 50, "h":15, "x": 50}}
],
"labels": ["x","y"]
};
select [sin(x), simsig(x)]
from number x
where x in heartbeat(0.01)
limit 1000

Plotting different streams in separate plots​

To plot the result from different streams at the same time (in separate plots) you have to use something called tags. Tagging simply means putting the value you want to plot in a vector and adding a string (tag) to the vector.

The following example shows a tagged value:

["my_tag", 123.4]

You provide a stream of tagged values to the visualization and it will send the value for different tags to separate plots. The JSON record for plotting tagged values is as follows:

{
"tagged": 1,
"tags": { "tag_accessor": "<tag-index>" },
"sa_plots":[
{"sa_plot": "<type>", "tag": "<tagname>", "value_accessor": "<value-index>"},
{"sa_plot": "<type>", "tag": "<tagname>", "value_accessor": "<value-index>"},
...
],
"<visualization-param-1>": <value>,
"<visualization-param-2>": <value>,
...
};

We see that we have a few new attributes which we need to specify:

  1. "tagged": This attribute is the flag for tagged data. If "tagged" is 1 then the visualization expects tagged data.

  2. "tags" and "tag_accessor": These attributes specify which index in the vector contains the tag. For example,

    "tags": { "tag_accessor": "0" }

    means that the first element in the result vector contains the tag. Note that tags use 0-indexing!

  3. "tag": This attribute is specified in each plot and determines which tag the plot will get its values from. For example,

    {"sa_plot": "Line plot", "tag": "my_tag", ...}

    means that we have a Line plot that will plot values tagged with "my_tag".

  4. "value_accessor": This attribute is specified in each plot and determines which element in the vector it will plot. For example, if we have a stream of tagged values

    ["my_tag", [1,2], 1.0]
    ["my_tag", [2,3], 1.2]
    ["my_tag", [2,1], 1.4]
    ...

    and we have an "sa_plot" record that has "value_accessor" set to "1"

    {"sa_plot": "Scatter plot", "tag": "my_tag", "value_accessor": "1"}

    then the plot will plot the following two-dimensional vectors since they are the second element of every vector tagged with "my_tag" (Note that tags are 0-indexed)

    [1,2]
    [2,3]
    [2,1]
Example

A Multi plot record for plotting three different streams (merged to a single stream of tagged values) in three separate plots could look like the following:

{
"tagged": 1,
"tags": {"tag_accessor": "0"},
"sa_plots": [
{"sa_plot": "Text Single", "tag": "runtime", "value_accessor": "1"},
{"sa_plot": "Line Plot", "tag": "simstream", "value_accessor": "1"},
{"sa_plot": "Line Plot", "tag": "sinus", "value_accessor": "1"}
]
};

The record says that we have three plots and the first will plot values tagged "runtime", the second will plot values tagged "simstream", and the third will plot values tagged "sinus". All plots will plot the second element of their respective tagged vectors ("value_accessor" is set to "1" for all plots and tags use 0-indexing).

Example: Plot different streams in separate plots​

Here we have an example of three different streams sin(heartbeat(0.1), simstream(0.01), and heartbeat(1). We use a function tags() to tag the output of each stream. Then we use the merge() function to merge the three tagged streams into a single stream of tagged values, that will look like this:

["sinus",0]
["simstream",0]
["runtime",0]
["simstream",1.03870379680811]
["simstream",-1.03259841340739]
["simstream",0.225737283403453]
...

Each value will be directed to the plot for the respective tag. The JSON record specifies that the tags are the first element in each vector (i.e., "tag_accessor" is set to "0"), and each plot plots the second value in the vector (i.e., "value_accessor" is set to "1" in all plots).

//plot: Multi plot
create function tags(Stream s, Object tag) -> Stream of vector
as select Stream of [tag,o]
from Object o
where o in s;

{
"tagged":1,
"tags": {"tag_accessor":"0"},
"sa_plots":[
{"sa_plot":"Text Single","tag":"runtime","value_accessor":"1", "title": "Runtime", "grid":{"w": 20,"h": 10}},
{"sa_plot":"Line Plot","tag":"simstream","value_accessor":"1", "title": "Simstream", "grid":{"w": 40,"h": 10}},
{"sa_plot":"Line Plot","tag":"sinus","value_accessor":"1", "title": "Sinus", "grid":{"w": 40,"h": 10}}
]
};
select merge([tags(sin(heartbeat(0.1)),"sinus"),
tags(simstream(0.01), "simstream"),
tags(heartbeat(1), "runtime")]) limit 500;

The streams don't have to be defined inline in the query. We could just as well define the streams as functions and tag the function output values.

//plot: Multi plot

/* Stream 1 */
create function sinus_stream() -> Stream of Real
as sin(heartbeat(0.1));

create function sign_stream() -> Stream of Charstring
as select Stream of sign
from Charstring sign, Real x
where x in sinus_stream()
and case when x > 0 then sign = "Positive"
else sign = "Negative"
end;

/* Stream 2 */
create function sign_state() -> Stream of Charstring
as changed(sign_stream());

/* Stream 3 */
create function sim_stream() -> Stream of Real
as simstream(0.1);

create function tags(Stream s, Object tag) -> Stream of Vector
as select Stream of [tag,o]
from Object o
where o in s;

{
"tags": {"tag_accessor":"0"},
"tagged":1,
"sa_plots":[
{"sa_plot":"Line plot","tag":"sin_stream","value_accessor":"1", "title": "Sin", "grid":{"w": 35,"h": 10}},
{"sa_plot":"Text Single","tag":"sign","value_accessor":"1", "title": "Sinus sign", "grid":{"w": 30,"h": 10}},
{"sa_plot":"Line plot","tag":"sim_stream","value_accessor":"1", "title": "Simulated stream", "grid":{"w": 35,"h": 10}}
]};
select merge([tags(sign_state(), "sign"),
tags(sinus_stream(), "sin_stream"),
tags(sim_stream(), "sim_stream")]) limit 150;

Plotting different data flows in separate plots​

Data flows are similar to streams but if we want to plot different flows in separate plots we can use the function subscribe:merge_tagged() which automatically tags the flows with the flow name. The Multi plot record format is the same as for streams. It is only how the data flows are tagged that differs.

Example: Plot different data flows in separate plots​

Here we illustrate how to plot two data flows in two separate plots. We start with a simulated stream and create two data flows from that stream. One data flow is the FFT over the stream, and one is just the streamed values as consecutive pairs in a two-dimensional vector. We will plot the FFTs in a Bar plot and the two-dimensional vectors in a Scatter plot.

To make this work we need to execute two code blocks. One code block to set up the flows and subscribes to the flows, and one code block to start the flows. The reason we need two code blocks is because the subscribe function that serves the plots will start listening on the flows and lock the process. When we run the second code block, the flows will start getting values and the subscribe function will start emitting results. When the subscribe function produces output the plots in the first code block will start showing data.

If we look at the first code block below we see that we create the function fft_and_2dvec_flows() that produces a sliding window over the stream from simstream() and uses the window to compute the FFT and build a two-dimensional vector of the first two values in the window. The FFTs are emitted on the flow "fft", and the two-dimensional vectors are emitted on the flow "2dvec".

At the bottom we have the subscribe:merge_tagged() function which tags the values in both flows and merges them to a single stream. The stream of tagged values will have the following format.

[1683186429.661,"2dvec",[-0.669168724800901,-0.689864869997291]]
[1683186429.661,"fft",[-0.91715594273681,0.0713944219914387,-0.081086167172199,...]]

We see that the tag is the second element, so we set the "tag_accessor" attribute to "1" (remember that tags use 0-indexing). The flow value is the third element, so we set the "value_accessor" to "2" for both plots.

Run the first code block start listening to the flows.

//plot: Multi plot
create function fft_and_2dvec_flows() -> stream of Vector
as select stream of win
from Vector win, Stream s, Vector f, Vector v
where s = timeout(simstream(0.01),5)
and win in winagg(s,100,1)
and f = emit_on_flow(rfft(win), "fft")
and v = emit_on_flow([win[1],win[2]], "2dvec")
limit 1000;

{
"tagged": 1,
"tags": {"tag_accessor": "1"},
"sa_plots": [
{"sa_plot":"Bar plot","tag":"fft","value_accessor":"2","title":"FFT","grid":{"w":50,"h":15}},
{"sa_plot":"Scatter plot","tag":"2dvec","value_accessor":"2","title":"2D Vec","grid":{"w":50,"h":15}}
]
};
subscribe:merge_tagged(["2dvec","fft"]);

Now that the first code block is running, run the the second code block to start populating the plots. The sink() function simply throws away the results from fft_and_2dvec_flows() so we don't have to see its output. We are only interested in the two plots in the first code block.

sink(fft_and_2dvec_flows());

Record generators​

There are a few predefined convenience functions for generating multi plot records in the multiplot: namespace that can be useful when analysing data.

  • multiplot:barplot_matrix
  • multiplot:generate_scatter_pcoords_grid
  • multiplot:scatter_matrix
  • multiplot:tagged_lineplots
A note on nomenclature

In statistical analytics terms each sensor represents an observable variable. Each time we read a measurement from a sensor we say that we sample (or observe) a value from the variable. In this documentation we sometimes also use the name dimension for a variable, which comes from the dimension each variable represents in the n-dim sample space.

Plotting grid of barplots for classified data​

When doing statistical analysis of sensor measurements that have been classified it can be of use to display the data in a grid of bar plots where each row presents a class and each column represents a sensor.

To plot the measurements for different classes we can use multiplot:barplot_matrix. It produces a multiplot record for a grid of barplots with classes on the grid y-axis and measurements for each sensor on the x-axis.

Parameters:

  1. Number of classes.
  2. Number of variables (dimensions).
  3. Vector of class names.
  4. Vector of variable (dimension) names.
  5. Height of plot windows.

The data format needs to be a vector of vector of vector of number, where the inner vector is a series of measurements from a specific sensor, each element of the middle vector represents a class, and the elements outer vector represents the variables.

So if we have kk classes c={1,2,...,k}c = \{1,2,...,k\} and nn sensors (variables) s={1,2,...,n}s = \{1,2,...,n\}, and the measurement vector veccsvec_{cs} is the measurements of sensor ss for class cc, then the data is on the format:

[
[ vec_11, vec_21, ..., vec_k1 ], // readings from sensor 1
[ vec_12, vec_22, ..., vec_k2 ], // readings from sensor 2
... // ...
[ vec_1n, vec_2n, ..., vec_kn ], // readings from sensor N
]

So the inner vector of vector can be seen as a matrix with the columns representing classes and the rows representing sensors/variables.

Example

Let's say that you have a weather station with five (5) different sensors (temperature, humidity, etc). Let's also say that the measurements are taken during three (3) different states of the environment (e.g., raining, sunny, snowing). We call the states classes.

To plot the data we first call multiplot:barplot_matrix with the number of classes (3), the number of variables (5), the names of the classes, the names of the variables (we use generic names in this example), and finally the height of the plots (200). After the record-generating function call we provide the data on the required format.

//plot: Multi plot
multiplot:barplot_matrix(3, 5,
["class1", "class2", "class3"],
["var-1", "var-2", "var-3", "var-4", "var-5"],
200,true);

// class 1 class 2 class3
[ [[1,2,3,4,5,6,7], [2,3,4,4,5,5,6], [2,3,4,8,9,9,7]], // var 1
[[7,6,5,4,3,2,1], [9,8,5,6,4,2,2], [9,6,5,3,5,3,5]], // var 2
[[2,2,3,4,2,2,3], [8,8,9,9,8,9,8], [22,21,15,13,16,11,10]], // var 3
[[7,3,4,5,6,1,2], [1,2,0,0,7,3,4], [2,4,9,8,3,3,4]], // var 4
[[2,3,5,4,6,8,9], [1,1,2,3,4,4,4], [6,8,9,5,2,1,6]]] // var 5

The result is a grid of bar plots with each row representing one class and each column representing one sensor.

Parallel coordinate and scatter plots side-by-side​

When doing statistical analysis of sensor measurements that have been classified it can be of use to display the data with parallel coordinates and scatterplots side-by-side.

To plot the data we can use multiplot:generate_scatter_pcoords_grid. It produces a multiplot record for a grid of plots with the first column being parallel coordinate plots of the data from each class and the second column being a 2D scatter plot of two specific variables.

Parameters:

  1. Vector of class labels.
  2. Vector of variable labels (dimensions).
  3. Vector of the two variable labels to use in the scatter plot (as provided in (2)).
  4. Label of variable to use for color map.
  5. Height of plot windows.

The data format needs to be a stream of vector of number, where the vectors are measurement vectors with one value from each sensor PREPENDED by two elements representing the ID (disregarded by plot) and CLASS.

So if we have KK classes, NN varibles with observed values sis_i where i={1,2,...,N}i = \{1,2,...,N\}, then the data is on the format:

[
[ 0, 1, s_1, s_2, ..., s_N ], // class 1
[ 0, 1, s_1, s_2, ..., s_N ], // ...
...
[ 0, 2, s_1, s_2, ..., s_N ], // class 2
[ 0, 2, s_1, s_2, ..., s_N ], // ...
...
[ 0, K, s_1, s_2, ..., s_N ], // class K
[ 0, K, s_1, s_2, ..., s_N ], // ...
...
]

Example

Let's say that you have a weather station with five (5) different sensors (temperature, humidity, etc). Let's also say that the measurements are taken during three (3) different states of the environment (e.g., raining, sunny, snowing). We call the states classes.

To plot the data we first call multiplot:generate_scatter_pcoords_grid with the vector of class labels, the vector of variable labels, the two variables that we want to scatter plot, the variable we want to use for color mapping, and finally the height of the plots (200). After the record-generating function call we provide the data on the required format.

//plot: Multi plot
multiplot:generate_scatter_pcoords_grid(
["class1", "class2", "class3"],
["id", "class", "var-1", "var-2", "var-3", "var-4", "var-5"],
["var-1", "var-2"],
"var-1",
200);

vstream(
[
[0,1,6,100,78,-21,8], // class 1
[0,1,3,110,70,-24,3], // ...
[0,1,2,120,64,-22,3],
[0,1,7,105,77,-28,2],
[0,1,3,130,80,-24,3],
[0,2,5,150,71,-35,1], // class 2
[0,2,2,155,82,-18,8], // ...
[0,2,5,160,84,-13,2],
[0,2,3,153,73,-16,5],
[0,2,5,166,64,-13,5],
[0,3,4,120,70,-35,4], // class 3
[0,3,1,118,72,-31,6], // ...
[0,3,2,112,77,-38,2],
[0,3,2,115,77,-37,7],
[0,3,7,117,66,-32,1]
]
);

The result is columns of plots with each row representing one class and the first column is a parallel corrdinate plot and the second is a scatter plot of two variables.

Geo JSON​

SA Studio supports visualization on Google Maps using Geo JSON. You can try out and build your own GeoJSON object at geojson.io.

Example of a single point:

//plot: Geo JSON
geojson:point(17.640617787837982,59.85822408829443,{})

A point with custom style and a label:

//plot: Geo JSON
geojson:point(17.640617787837982,59.85822408829443,
{"style": {"label": "SA HQ"}})

By default all added features are removed when new data arrives:

//plot: Geo JSON
geojson:point(17.630586326122284, 59.859743971785704,
{"style": {"label": "Old SA HQ"}});
geojson:point(17.640617787837982,59.85822408829443,
{"style": {"label": "SA HQ"}})

This can be remedied by setting persistent to true in the properties Record:

//plot: Geo JSON
geojson:point(17.630586326122284, 59.859743971785704,
{"persistent": true,"style": {"label": "Old SA HQ"}});
geojson:point(17.640617787837982,59.85822408829443,
{"persistent": true,"style": {"label": "SA HQ"}})

If you wish to move a point you can set persistent to true and add an id property:

//plot: Geo JSON
geojson:point(17.630586326122284, 59.859743971785704,
{"persistent": true,"id":"office",
"style": {"label": "SA HQ"}});
geojson:point(17.630586326122284, 59.859743971785704,
{"persistent": true,
"style": {"label": "Old SA HQ"}});
geojson:point( 17.646524012088776, 59.858205232414065,
{"persistent": true,
"style": {"label": "Uppsala train station"}});

geojson:point(17.640617787837982,59.85822408829443,
{"persistent": true,"id":"office",
"style": {"label": "SA HQ"}});
Note

When using an id to track points you can click on it to mark it with a different color.

Geo JSON also supports Lines:

//plot: Geo JSON
geojson:line([[17.630586326122284, 59.859743971785704],
[17.640617787837982,59.85822408829443],
[17.646524012088776, 59.858205232414065]],
{"style": {"strokeColor":"red", "fillColor": "red",
"strokeWeight": 1}});

Polygon:

//plot: Geo JSON
geojson:polygon([[[17.630586326122284, 59.859743971785704],
[17.640617787837982,59.85822408829443],
[17.646524012088776, 59.858205232414065],
[17.630586326122284, 59.859743971785704]]],
{"style": {"strokeColor":"red", "fillColor": "red",
"strokeWeight": 1}});

Using all we've seen so far:

//plot: Geo JSON
geojson:point(17.630586326122284, 59.859743971785704,
{"persistent": true,"id":"office",
"style": {"label": "SA HQ"}});
geojson:point(17.630586326122284, 59.859743971785704,
{"persistent": true,
"style": {"label": "Old SA HQ"}});
geojson:point( 17.646524012088776, 59.858205232414065,
{"persistent": true,
"style": {"label": "Uppsala train station"}});

geojson:point(17.640617787837982,59.85822408829443,
{"persistent": true,"id":"office",
"style": {"label": "SA HQ"}});
geojson:polygon([[[17.630586326122284, 59.859743971785704],
[17.640617787837982,59.85822408829443],
[17.646524012088776, 59.858205232414065],
[17.630586326122284, 59.859743971785704]]],
{"persistent": true,
"style": {"strokeColor":"red", "fillColor": "red",
"strokeWeight": 1}});

geojson:line([[17.630586326122284, 59.859743971785704],
[17.640617787837982,59.85822408829443],
[17.646524012088776, 59.858205232414065]],
{"persistent": true,
"style": {"strokeColor":"blue",
"strokeWeight": 10}});

This can of course be made into a feature collection as well:

//plot: Geo JSON
geojson:collection(
[geojson:point(17.630586326122284, 59.859743971785704,
{"style": {"label": "Old SA HQ"}}),
geojson:point( 17.646524012088776, 59.858205232414065,
{"style": {"label": "Uppsala train station"}}),
geojson:point(17.640617787837982,59.85822408829443,
{"style": {"label": "SA HQ"}}),
geojson:polygon([[[17.630586326122284, 59.859743971785704],
[17.640617787837982,59.85822408829443],
[17.646524012088776, 59.858205232414065],
[17.630586326122284, 59.859743971785704]]],
{"style": {"strokeColor":"red", "fillColor": "red",
"strokeWeight": 1}}),
geojson:line([[17.630586326122284, 59.859743971785704],
[17.640617787837982,59.85822408829443],
[17.646524012088776, 59.858205232414065]],
{"style": {"strokeColor":"blue",
"strokeWeight": 10}})])

Lets simulate two persons walking (well, flying) from Old SA HQ and Uppsala Train Station to the current SA HQ.

//plot: Geo JSON
set :old_sa_hq = [17.630586326122284, 59.859743971785704];
set :new_sa_hq = [17.640617787837982,59.85822408829443];
set :station = [17.646524012088776, 59.858205232414065];

create function interpolate_between(Vector of Number p1,
Vector of Number p2,
Integer steps,
Number pace,
Charstring name)
-> Stream of Record
as select stream of geojson:point(p,
{"persistent": true,
"id": name,
"style": {"label": name}})
from Integer i, Vector delta, Vector p
where delta = (p2-p1)/steps
and i in diota(pace,1,steps)
and p = p1+delta*i;



merge(interpolate_between(:old_sa_hq, :new_sa_hq, 10,1, "Johan"),
interpolate_between(:station, :new_sa_hq, 4,1, "Erik"));

See OSQL API -> Geo JSON for more info on GeoJSON in SA Studio