/DataVisual

3D Data Visualization Design Pattern Implemented with JavaScript

Primary LanguageJavaScriptMIT LicenseMIT

DataVisual

DataVisual is a design/programming pattern for developing data visualizations with WebGL 3D assets.
The pattern is implemented in JavaScript using the Three.js framework, but is applicable to other frameworks like Babylon.

With a DataVisual you can dynamically visualize individual meshes, that have unique materials associated with them, in a Three.js scene.

Traditional dataset processing can be used on a DataVisual leveraging JavaScript's robust array methods; most notably MapReduce patterns.

Hello DataVisual demo (source) is available on Observable and in the repository.

DataVisual Demonstrated With a U.S Navy Salvage Robot (aquaBot) Use Case

Screen Shot of Demonstration

The demonstration illustrates DataVisual's features with a use case simulating a U.S. Navy (Go Navy! Beat Army) aquaBot determining an optimal salvage retrieval strategy for objects floating and submersed out at sea.

aquaBot utilizes information about the salvage items in a data array partnered (joined) with the 3D visual's spatial attributes to maximize the salvage with a fixed amount of fuel.

The 3D visual was developed with Blender and exported as a glTF file. Three.js's GLTFLoader is used in the demonstration to load the 3D visual and then dynamically visualize it.

The visualization animates the salvage strategy and leverages several of DataVisual's features including:

  • Maintaining join context when sorting the join object;
  • Referencing joined properties from both the data and visual;
  • Dynamic data coloring/visualization using D3.js.
  • Rendering only objects that will be salvaged with a data-driven algorithm;
    • All objects can be displayed/rendered with a user-driven (aquaBot's operator) option for human validation;
  • Use of the built-in tooltip and mouse event handler.

Prerequisites

If using the Chrome browser with file system access, the --allow-file-access-from-files flag must be used.
See: How to set the allow-file-access-from-files flag option in Google Chrome

Installing

Download zip of code, unzip to a folder and launch index.html from a web server or from the file system with a WebGL enabled browser.

The demonstration application will intially prompt for the type of glTF file to utilize:

Screen Shot of glTF Selection

  • gltf/seaScape.gltf utilizes data as a separate object in the join.
  • gltf/seaScapeEmbeddedData.gltf utilizes embedded data within the glTF file; extracting it first before implementing the join.

Documentation

Several Observable Notebooks in the DataVisual Collection.

Design Pattern Usage Illustrated in the Demonstration

DataVisual with Data in an Object Not Embedded in the Visual

Assuming the following:

  • A JavaScript data object in the following format:
const data = [{name: "object1", value: value1, ...} ,{name: "object2", value2, ...}, ... }];
  • A glTF file loaded into the variable visual with name attibutes for the meshes that match the name attributes for the data object;

Create a DataVisual object with the following syntax:

const datVisual = new dataVisual();
datVisual.joinDataToVisual(data, visual);

// Alternate instantiation pattern supported by the ES6 class:
// const datVisual = new dataVisual({data, visual});

DataVisual with Data Embedded in the Visual

Three.js, as well as the glTF specifications, provide for a strategy to extend the visual with data that can be visualized:

  • With Three.js it is the userData property.
  • With gltF it is the extras property.

Instantiating a DataVisual in the following format creates a dataVisual object from a visual when the data is embedded in the userData property.

const datVisual = new dataVisual();
datVisual.joinDataToVisual(visual);

// Alternate instantiation pattern supported by the ES6 class:
// const datVisual = new dataVisual({visual});

The dataVisual has the following methods and properties:

Methods:

Method Description
.joinDataToVisual(data, visual) Join data to a visual's mesh utilizing each object's name property
.joinDataToVisual(data, visual, dataKey, visualKey) Join data to a visual utilizing data's dataKey property and visual's visualKey property. visualKey can be a direct property of a mesh, or its .userData property.
.joinDataToVisual(visual) Creates a dataVisual object from a visual when the data is embedded within the userData property for mesh(es).
.getJoinByUUID(uuid) Get a joined row by referencing a mesh's uuid. This method is very helpful with raycasting techniques.
.getJoinByUUID(uuid, "index") Get a joined row's index by referencing a mesh's uuid. This method is very helpful with raycasting techniques.
.getJoinByKey(dataKey) Get a joined row by referencing a dataRow's dataKey.
.getJoinByKey(dataKey, "index") Get a joined row's index by referencing a dataRow's dataKey.
.getDataColumn(column) Get an array of a specified column (attribute/property) form the dataVisual's data.
.setColorVisualObj(visualObj,color) Set the material.color property for a mesh. The method utilizes the Three.js traverse method to set color for any children the mesh may have as well.
.setColorByJoinIndex(index,color) Set the material.color property for a mesh by referencing the index of the join. The method invokes .setColorVisualObj for the given index.
.isolateObjects(visualObjs) Isolates makes solely the visual objects in the visualObjs array visible; visible = true;
.showAll() Shows all visual objects in the DataVisual visible; visible = true;
.addToolTip(renderer, camera[, getToolTipText]) Implements a custom tooltip assuming the following parameters: (See Hello, DataVisual! for an example.)

renderer: A Three.js WebGLRenderer object that has been attached to an HTML parent element.

camera: A Three.js camera object

getToolTipText: An optional tooltip text callback function that will be passed two parameters: references to the join row and the dataVisual instance itself. With the second parameter (dataVisual) the callback has access to all the object's properties.

The getToolTipText callback function must return a DOMString that will be assigned to the tooltip's innerHTML property.
.onHover(renderer, camera, handleEnter [,handleLeave]) Implements mouse hover handlers for joined objects. Both handleEnter and the optional handleLeave callback funtions will be will be passed two parameters: references to the join row and the dataVisual instance itself. With the second (dataVisual) parameter the callback has access to all the object's properties.
.onDoublClick(renderer, camera, handleDblClick)

Implements a mouse double-click event handler for joined objects. The handleDblClick callback function/handler will be passed two parameters: references to the join row and the dataVisual instance itself. With the second (dataVisual) parameter the callback has access to all the object's properties.

Properties:

Property Description
.join A join object (array). Each element of the array will be an object referencing a joined (matching) dataRow and visuaObj.
.join[n].dataRow A reference to nth data row from the original data array that corresponds to the joined nth (matching) visualObj.
.join[n].visualObj A reference to the nth Three.js mesh being 'joined' to that corresponds to the joined nth (matching) dataRow.
.data The original data participating in the 'join'.
.visual The original visual participating in the 'join'.
.dataKey The data key attribute being used to join to a corresponding mesh in the visual.
.visualKey The visual key attribute being used to join to the visual's mesh. Property visualKey can be a direct property of a mesh or a property of the mesh's .userData member.
.nonMatchingDataKeys An array holding any dataKey(s) that did not join/match when the joinDataToVisual method was invoked.

Built With

Author

License

Free to all non-profit organizations. Businesses can email for licences details.