Skip to content

JSON ViewModel Specification

Mark edited this page Mar 14, 2020 · 5 revisions

VisLab JSON ViewModel

Here you will find the set of data structures used by Visualisation Lab (VisLab) to simplify the creation of visualisations whether using existing libraries, components or custom code.

Discovering what form to get data into for visualisation involves significant work. The aim of JSON ViewModel is to reduce this by helping you to avoid re-inventing this wheel, to save time documenting what you use, and improve interoperability and code maintenance.

Adopting these data structures will simplify the process of constructing a visualisation using common JavaScript visualisation libraries such as Vega, Vega-Lite and D3, or your own custom visualisation component. It should also improve interoperability between systems and libraries, as well as saving time on documentation.

To summarise the benefits of using the VisLab JSON ViewModel:

  • no need to invent your own structure (choose the appropriate ViewModel instead)
  • visualise by following an example for your favourite library (examples wanted!)
  • no data structures to document as you can refer to or copy this document
  • easy code re-use and maintenance

Consider this a potential de-facto standard for visualisation of data using JavaScript, or by all means go your own way! But if you use these structures you should be able to get your data into one of a large range of visualisations with minimum time and effort. This could be useful even if in the long run you develop your own JSON representations.

Interactivity

An important goal for VisLab is the ability to interact with data through the visualisations, and to make this as intuitive as possible. While the data structures described here don't go into that, you will see how they can be applied to support interactivity within VisLab itself.

Background

When trying to decide on suitable data structures for use in VisLab I couldn't find an official standard or any comprehensive documentation of the structures used or recommended in various visualisation libraries. There seemed to be some coherence across different libraries but nothing definitive.

Here I try to identify a reasonable set of JSON data structures for use in visualisation, and to document them for use by anyone.

I chose to base these structures around conventions I found in use with Vega and Vega-Lite, D3 and so on, and to make it trivial to take data using the models described below and feed it to one of these libraries to create any one of a large number of visualisations.

For example, if you want to use something supported by Vega-Lite you'll be able to plug a VisualModel described here directly into a Vega-Lite schema just by merging the two. On the other hand it is easy to use these same models with any other library, or your own visualisation components. VisLab uses a combination of custom visualisations including some built with D3, as well as some provided by Vega and Vega-Lite using the Vega-Lite runtime.

Relationship to Vega and Vega-Lite

You are not tied to using Vega or Vega-Lite if you adopt these structures but I warn you, it might just be too easy not to do so!

If you know Vega or Vega-Lite, you will appreciate how easy it is to combine these models with a Vega/Vega-Lite schema to create one of many different styles of visualisation. Essentially the JSON ViewModel is a Vega schema with everything but the data to be modelled stripped out, at the same time as tying the data part down so anyone can work with the same structure.

For those not familiar with Vega/Vega-Lite, a Vega schema is a JSON object with properties to identify a data source, transform it, select a visualisation and control how the data is presented. The data to be visualised will be present in the 'values' property of a schema dataset.

The relevant part of the Vega schema looks like this:

let schema = {
  data: [{
    name: "Dataset Name",
    values: Object,
    // Optional Vega dataset properties
  }]
  // Other Vega schema properties...
}

The 'data' array of a schema can hold more than one dataset, each of which can be referenced by other parts of the schema via the dataset 'name'. There are many other properties that make up a Vega schema, but these are not shown above for clarity. The only part of the schema that ViewModel represents is the object which is shown above with a 'values' property (more on this below).

One or more datasets must be present in the 'data' property of a Vega schema. As you can see above, the 'data' property is an array of objects, each of which must constrain a 'values' property. The 'values' property might be an array or an object.

The point though is that with Vega, there is no defined structure for the 'values'. That makes it easy to load data from different sources and then configure the schema to transform it ready for visualisation. Whereas a JSON ViewModel gives you the option of adopting a particular structure that can be used with any schema also designed to accommodate it. That avoids the need to write or customise a new schema at the cost of getting the data into the chosen ViewModel format. Both approaches are useful, so you can see the JSON ViewModel as giving you an extra set of off the shelf visualisations (which can later be customised), alongside a that of creating a new Vega/Vega-Lite schema for a new input data structure.

Use within VisLab

Visualisation Lab makes use of these standards and so provides a set of examples (in Svelte) of how they can help build a set of components for that allow for transformation, filtering, selection of data for visualisation with an interactive user interface. If you don't know Svelte, don't worry because it is very easy to understand the structure and operation of Svelte code.

This 'specification' and VisLab are a work in progress, which means that feedback and suggestions, and contributions (!) are welcome in all areas (see 'Feedback' above).

Within VisLab I envisage using a set of ViewModel classes to generate each model type (e.g. VMGraph) and then to add transform ViewModel Transform (VMT) class to adapt each for a particular view component schema. For example: SourceResult -> VMGraph -> VMGraphToVega -> ViewVegaForceGraph

The latest deployed demo of VisLab is live at vlab.happybeing.com.


JSON ViewModel

The JSON structures defined below are in many cases based on those suggested for the 'values' object of the Vega/Vega-Lite schemas, taken from the official documentation. This makes them very easy to use with the Vega-Lite runtime, to generate one of the many Vega/Vega-Lite visualisations, which can later be customised through your Vega/Vega-Lite schema or replaced by a completely new visualisation (e.g. using custom rendering or a library such as D3).

ViewModel Structure

A ViewModel has the following form, where the structure of the 'values' object defined according the one of the JSON ViewModel structures (below):

let jsonModel = {
  values: Object
  // Optional ViewModel properties...
}

The 'values' property holds the data to be visualised in a fairly raw form, for example after loading from a source such as a CSV file.

Using A ViewModel Object

The ViewModel structures defined below have been kept simple to make it easy to generate a visualisation using any library (or Vega schema) which caters for these particular structures, or for you to write your own custom visualisation components.

In the case of Vega, you can draw on a library of schemas designed for use with each ViewModel and merge your jsonModel object into it.

Vega/Vega-Lite: some ViewModels could have additional properties alongside 'values', in which case when using a Vega schema it will be up to you to merge these into the relevant dataset object.

Even if you follow the JSON ViewModel structures, you can still 'roll your own' fancy schema, and the model gives you a quick start. Either way, it makes it quick and easy to get a visualisation, which can be useful even if the intention is to create something customised in the end.

Feedback

Feedback is very welcome so if you have comments or suggestions please use this issue and if you make use of these structures it would be greate to hear from you.

JSON ViewModel Structures

JSON ViewModel types so far include:

  • vm-tabular-json for tables (e.g. from a spreadsheet in CSV format)
  • vm-graph-json for graphs (nodes and links)
  • vm-tree for a hierarchy of nodes with an optional value property
  • vm-series-json for a series of values

The following is not intended as a ViewModel, but an input which can be transformed into a ViewModel such as vm-graph-json. It is included because it is part of the full set of models used to represent data by VisLab, but can be ignored for most applications.

  • raw-graph-rdfdataset for RDF graphs loaded using RDF/JS

Each of these is defined below.

Note that in the following the 'VisLab class' is not part of this specification, but referenced as an example of code which implements the model as a JavaScript class.


ViewModel: vm-tabular-json

{ 
  values: [
    [v1, v2, v3 ...],	// Row 1, normally the column headings (see 'header' property)
    [v1, v2, v3 ...],	// Row 2
    [v1, v2, v3 ...],	// Row 3
  ]
  header: [h1, h2, h3 ...],	// Only present if row 1 does NOT contain column headings
}

VisLab code example: class VMTable

Examples:

  • TODO

ViewModel: raw-graph-rdfdataset

{
	values: Object	// RDF/JS Dataset
}

VisLab code example: class VMGraph

Examples:

  • TODO

ViewModel: vm-graph-json

{
	values: { nodes: [], links: [] }
}

Where:

	a node is { id, ...properties }	// Optional properties can affect visualisation, such as 'label'
	a link is { source, target, ...properties }	// source = source node id, target = target node id

Optional node properties:

  {
    group:  // An identifier which indicates membership of a group or category
  }

Optional link properties:

{
  strength: Number, // A positive value suggesting the weight or significance of a link
}

VisLab code example: class VMGraph

Examples:

  • TODO

ViewModel: vm-tree-json

{
	values: [],
	quantityField: String,	// [optional] the name of the field on a node which contains a numeric quantity
}

where values is an array of node objects:

{
	index: Number, 	// Index of this node
	parent: Number, // Index of parent node. The root node uses its own index as parent
	<quantity>: Number,	// [optional] a numeric quantity associated with the node (see ViewModel.quantityField)
}

☐ how does this compare to D3 tree (e.g.d3-hierarchy)?

VisLab code example: class VMTree

Examples:

  • TODO

Note: For Vega, a tree will be generated from the above 'raw' values as follows. It may be useful for other View implementations to follow this convention, but there's no need to do this unless you want to maintain compatibity with VisLab (e.g. by making use of one of the components that will be published as VisLab is developed).

values: [{
  index: Number, 
  parent: Number,
  x: Number,
  y: Number,
  depth: Number,
  children: Number
}]

ViewModel: vm-series-json

{
  values: []  // TODO
}

Where:

VisLab code example: class VMSeries

Examples:

  • TODO

ViewModel: vm-timeline-json

TODO is this needed?

{
  values: []  // TODO
}

Where: ???

VisLab code example: class VMTimeline

Examples:

  • TODO

TODO

More possibilities to think about!

Chart - Line


Chart - Pie


ViewModel: vm-geo-json

See GeoJSON on Wikipedia