JS widgets for interactive chord and tree visualizations for hierarchical + connected data πŸš€πŸ©πŸŽ‰
JavaScript CSS
Switch branches/tags
Nothing to show
Clone or download

README.md

Xoces: Visualizing curricular data

Xoces: Chord & Tree visualization widget

Chrome Firefox IE Edge Safari
Latest βœ” Latest βœ” Latest βœ” Latest βœ” Latest βœ”
37+ βœ” 35+ βœ” 10+ βœ” 13+ βœ” 8+ βœ”

What and why

Xoces is a JavaScript widget for visualizing data that have both hierarchical levels and also relationships "within the same level". For example, you have learning outcomes grouped into subjects and you want to visualize how your outcomes relate to each other. Or you have actors and movies grouped by A/B/C/D-lists and you want to see how those all relate to each other.

Why use Xoces We want to save people time by providing a neat but powerful, configurable visualization that works more or less out of the box. You could code it from scratch from d3, but we think you'll find the API pretty easy to use. Just include the widget in your code or HTML, specify your data, and you're good to go.

See what the visualization looks like -- we have a few demos here.

Resources

Installing

If you use NPM and want to require Xoces as a module:

npm install --save xoces

Xoces has dependencies and needs these dependencies to be installed and require'd into your environment:

npm install --save jquery react react-dom redux react-redux

To require Xoces in your ES6:

import xoces from 'xoces'

Alternatively, if you use CommonJS module style, you can do:

var xoces = require('xoces')

UMD

If you use the UMD bundle, everything is bundled along with you, so you don't need to install dependencies and can just do:

import xoces from 'xoces/umd/xoces-umd'

If you want to download the standalone bundle and load it into your HTML, this will makes the xoces variable globally available. If you're not sure what NPM / CommonJS / ES6 is, this option is probably for you.

<html>
  <body>

  <script src="/scripts/xoces-umd.js"></script>
  <script>
    console.log('xoces is loaded!', xoces);
    var config = {...};
    var myWidget = xoces.widgets.XocesWidget.new(config);
  </script>
  </body>
</html>

CDN

You can also load it directly from CDN:

<!-- Loading a fixed version, e.g. 1.1.51 -->
<script src="https://unpkg.com/xoces@1.1.51/umd/xoces-umd.js"></script>

<!-- or load the latest  -->
<script src="https://unpkg.com/xoces/umd/xoces-umd.js"></script>

Getting started

Xoces lets you choose from 3 widgets: XocesWidget, ChordWidget, or TreeWidget.

The ChordWidget gets you the chord visualization. chord visualization

The chord component visualizes one level of the hierarchy at one time. The major segments (arcs) are the entities that are in that level, and the minor arcs are the entities that are children of that entity. For example, in the below example, there are 5 arcs representing 5 programs, and the highlighted class of Circuits & Electronics is a subarc: xoces arcs and sub-arcs

Whenever you click on a piece, the chord visualization changes to display the next level down. Confused? Take some time to explore our examples and keep reading to see what "hierarchy" means.

The TreeWidget gives you the tree visualization: tree visualization

The tree component computes a rank for each entity and arranges them in order of increasing rank. So, the top-most entity is the "beginning" and the bottom-most entity has the "most things going into it".

The XocesWidget gets you the chord and tree visualization by displaying entities in tree view when you're at the bottom of the hierarchy. chord-tree visualization

To initialize a widget, call:

var myWidget = xoces.widgets.XocesWidget.new({});

Of course this doesn't quite work -- if you inspect the console, xoces tells you that you're missing mandatory configuration settings. At minimum, you need to specify:

var myWidget = xoces.widgets.XocesWidget.new({

  // in this made-up example, we have a top level group. Within this top-level group, we have smaller groups.
  // ...within groups, we have teams. Within a team, we have people.
  hierarchy: ['top-level-group', 'group', 'team', 'person'],     
  data: {
    entities: [

      // let's start with two entities. 'id' and 'type' fields are mandatory.
      {
        id: 'entity1',
        type: 'top-level-group',
        nameForDisplay: 'i am the first entity'
      },
      {
        id: 'entity2',
        type: 'group',
        nameForDisplay: 'another entity'
      },
    ],
    relationships: [

      // this relationship points from entity2 ---> entity1, saying that entity2 'has_parent_of' entity1
      {               
        id: 'r1',
        type: 'has_parent_of',
        sourceId: 'entity2',
        parentId: 'entity1'
      }
    ]
  },

  // we choose the key 'nameForDisplay' for displaying the entity
  entityLabelKey: 'nameForDisplay',

  // we tell xoces that grouping relationships have type 'has_parent_of'
  // and that relationships have keys 'sourceId' and 'targetId' that point to the source and target respectively
  relationship: {
    parentType: 'has_parent_of',
    sourceRef: 'sourceId',
    targetRef: 'targetId',
  },
});

API

xoces.widgets.XocesWidget()

The first step is to create a widget by passing in a config object.

var myWidget = xoces.widgets.XocesWidget.new(config);

widget.render()

Calling this method renders the widget onto screen. This method expects either the container or the id of the container element that wraps the widget. If the name you provided is not found, it will create an element and append it to the body. We recommend you always specify a container.

myWidget.render({
  container: string
})

configuration

The config argument passed into xoces.widgets.XocesWidget(config) is an object with these fields (and their default values):

var config = {
  data: {
    entities: [Entity],                  // required!
    relationships: [Relationship]         
  },
  hierarchy: [],                         // required!
  currentLevelEntity: null,
  entityLabelKey: '',                    // required!
  nodeLabelKey: '',                     
  relationship: {
    parentType: '',                      // required!
    sourceRef: '',                       // required!
    targetRef: '',                       // required!
  },
  width: '100%',
  height: 500,
  colorScheme: 'light',                  // 'light' or 'dark',
  onMouseOverDirection: 'outgoing',
  onMouseOverFinish: function(entity) {},
  onMouseOutFinish: function(entity) {},
  onClickFinish: function(entity) {}
}

Read more below on each field.

configuration.data

This field is an object with 2 fields: entities and relationships. entities must be an array of Entity objects (see below for more detail), and relationships must be an array of Relationship objects.

configuration.data.entities

An Entity object is just a plain-old JavaScript object, with two mandatory properties: id and type. The id field is a unique identifier for the entity and must not be repeated. The type field specifies what type of entity it is.

For example, this is a valid entity:

var validEntity = {
  id: 'entity1',
  type: 'course',
  someOtherProperty: 'hello world!'
}

configuration.data.relationships

A Relationship object is also just a plain-old JavaScript object, with three mandatory properties: type, sourceId, targetId.

configuration.hierarchy

This field is required. This is an array of the types of entities, ordered by increasing granularity. This specifies how your entities are supposed to be nested. For example, your data model may look like:

- institution
  - school
    - department
      - course

Your hierarchy array would then be:

['institution', 'school', 'department', 'course']

configuration.colorScheme 'light' or dark. The light scheme works better for print outs, while the dark scheme looks better for presentations.

configuration.width Sets the width of the entire widget.

configuration.height Sets the height of the entire widget. We recommend at least a 500px height.

configuration.onMouseOverDirection incoming or outgoing, or both. Controls which entities are shown when you mouseover an arc. Default is outgoing

configuration.onMouseOverFinish A callback function invoked when the user mouseovers a subarc or tree node. The function is invoked with one argument -- the object of the mouseover'ed entity

configuration.onMouseOutFinish A callback function invoked when the user mouses-out out of a subarc or tree node. The function is invoked with one argument -- the object of the mouse-out entity

configuration.onClickFinish A callback function invoked when the user clicks on a subarc or tree node. The function is invoked with one argument -- the object of the clicked entity.

xoces.widgets.TreeWidget() The config argument passed into xoces.widgets.TreeWidget(config) is an object with these fields (and their default values):

var config = {
  data: {
    entities: [Entity],                  // required!
    relationships: [Relationship]         
  },
  entityLabelKey: '',                    // required!
  relationship: {
    sourceRef: '',                       // required!
    targetRef: '',                       // required!
  },
  width: '100%',
  height: 500,
  colorScheme: 'dark',                  // 'light' or 'dark'
  nodeLabelKey: null,                    // [String]
  nodeColor: null,                       // [String | Function] e.g., 'black', '#ff0033', or a function that returns a valid color
  limitToSameParentInTree: false,        // [Boolean] that indicates whether nodes that are external to the parent of the given nodes should be drawn. most likely you want to leave it as the default value (false),
  onMouseOverFinish: function(entity) {},
  onMouseOutFinish: function(entity) {},
  onClickFinish: function(entity) {}
}

configuration.nodeColor [String | Function] If passed a function, the function is called with the current entity and is expected to return a valid color string.

Examples

In the dist/ folder, there are 3 examples that are loaded into dist/index.html:

  • example1.js: Uses a made-up data set
  • example2.js: Uses the Singapore University of Technology & Design core curriculum dataset. A smaller data set of hundreds of outcomes -- interactive visualization.
  • example3.js: Uses a subset of the Massachusetts Institute of Technology outcomes mapping dataset. This data set has thousands of outcomes -- interactive visualization

Pull requests and bugs

Please contribute or file requests! We respond within 24 hours.

Feedback

Contact us at the MIT Mapping Lab [mapping-lab at mit.edu]