TreeMap API

Purpose of This Document

Welcome to the Macrofocus TreeMap API. TreeMap API is a Java/Swing and JavaScript/HTML5 implementation of the treemap visualization technique. This developer guide is designed for developers who want to learn how to use TreeMap API in their applications.

Treemaps Basic

Treemaps graphically represent information about objects by dividing the display into areas (typically rectangles) that are proportional to the size of each object. It also enables the display of hierarchical information by nesting each area into subregions. It was invented and first used in the early 1990s by Ben Shneiderman at the University of Maryland for the management of the disk space of his server. Treemaps display rows of data as groups of squares that can be arranged, sized and colored to graphically reveal underlying data patterns. This visualization technique allows users to explore and easily recognize complicated data relationships.

TreeMap API Features

Macrofocus TreeMap comes with an extended set of features, including:

  • Support for popular Swing TableModel (Java) and JavaScript objects/JSON (JavaScript)

  • Flexible configuration of size, color, and labels of the treemap elements

  • Complete set of layout algorithm (including solid squarified and aesthetically-pleasing circular layout algorithm)

  • Cushion rendering to reveal hierarchy intuitively

  • Zoomable user interface, including drilling

  • Many options to fine-tune the appearance of the display

  • Flexible hierarchy definition to create custom aggregation schemes

  • Filtering support

  • Details on demand provided with popups

  • Useful for small datasets already, but scales to 100'000s of data objects

TreeMap API Quick Start

This section contains some examples that demonstrate how easy it is to get up and running with TreeMap API. The following sections provide much more detail about how to configure your charts and which features are available, but most developers are eager to get something working quickly, so here is a quick working example.

The code below will produce the output below. One can see that the area of each rectangle is proportional to the “Value” column and the colors assigned depending on the “Strength” column. The values of the “Name” column are used to label each rectangle by filling the entire width and scaling the font accordingly. While the content of this example is meaningless, it highlights the key principles and one should be able to extrapolate the use of such a visualization technique when applied to budget, sales, quality control, fraud detection, and financial data and any other areas where getting the big picture reveals pattern.

Hello from the Treemap World!

Hello from the Treemap World!

Java/Swing

To use the TreeMap API for Java/Swing, place the macrofocus-treemap.jar, macrofocus-common.jar, macrofocus-visualization.jar, and macrofocus-slider.jar libraries in your class path. An efficient starting point is to instantiate the com.macrofocus.treemap.TreeMap component.

public class Hello {
    public static void main(String[] args) {
        // Defining the data, column names and types
        Object[][] data = new Object[][]{
                {"Hello", 12, 3.0},
                {"from", 11, 4.0},
                {"the", 9, 5.0},
                {"TreeMap", 8, 6.0},
                {"World!", 7, 7.0},
        };
        Object[] columnNames = new Object[]{"Name", "Value", "Strength"};
        final Class[] columnTypes = new Class[]{String.class, Integer.class, Double.class};

        // Creating a standard Swing TableModel
        TableModel tableModel = new DefaultTableModel(data, columnNames) {
            public Class<?> getColumnClass(int columnIndex) {
                return columnTypes[columnIndex];
            }
        };

        // Creating the TreeMap
        TreeMap treeMap = new TreeMap(tableModel);

        // Tuning the appearance of the TreeMap
        treeMap.setAlgorithm(AlgorithmFactory.SQUARIFIED);
        treeMap.setSizeByName("Value");
        treeMap.setColor(2);
        treeMap.setBackgroundByName("Name");
        treeMap.setLabels();

        // Creating a frame to display
        final JFrame frame = new JFrame("Hello from the TreeMap World!");
        frame.setSize(600, 600);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(treeMap);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

JavaScript/HTML5

To use the TreeMap API for JavaScript/HTML5, place the content of the TreeMap directory on your web server and start the loading of the module using the TreeMap/TreeMap.nocache.js script. The loading of the module happens asynchronously. Once it is loaded, then the treeMapModuleLoaded() `callback method is called. The `com.macrofocus.treemap.TreeMap component is now ready to be instantiated and should be attached to an HTML element by passing its id.

<!DOCTYPE html>
<html style="height: 100%; margin: 0 0 0 0">
<head>
    <title>TreeMap API for JavaScript</title>

    <link type="text/css" rel="stylesheet" href="TreeMap.css">

    <!-- This script is required bootstrap stuff.   -->
    <script type="text/javascript" language="javascript"
            src="TreeMap/TreeMap.nocache.js"></script>
    <script type="text/javascript" language="javascript">
        function treeMapModuleLoaded() {
            com.macrofocus.treemap.TreeMap.setLicenseKey("My Company", "ABC12-ABC12-ABC12-ABC12-ABC12-ABC12");
            treeMap = new com.macrofocus.treemap.TreeMap("treeMap");
            treeMap.loadJsonString("{\"data\": [" +
                "{\"Name\": \"Hello\", \"Value\": 12, \"Strength\": 3.0}, " +
                "{\"Name\": \"from\", \"Value\": 11, \"Strength\": 4.0}, " +
                "{\"Name\": \"the\", \"Value\": 9, \"Strength\": 5.0}, " +
                "{\"Name\": \"TreeMap\", \"Value\": 8, \"Strength\": 6.0}, " +
                "{\"Name\": \"World!\", \"Value\": 7, \"Strength\": 7.0}" +
            "]}");
        }

        function treeMapModelLoaded() {
            treeMap.setToolTipByNames("Name", "Industry", "Value", "Strength");
            var treeMapModel = treeMap.getModel();
            var treeMapSettings = treeMapModel.getSettings();
            var fieldSettings = treeMapSettings.getDefaultFieldSettings();
            fieldSettings.setAlgorithm(com.macrofocus.treemap.AlgorithmFactory.SQUARIFIED);
        }
    </script>
</head>
<body style="width: 100%; height: 100%; border: 0 0 0 0; margin: 0 0 0 0; overflow: hidden">
<!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
<noscript>
    <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
        Your web browser must have JavaScript enabled
        in order for this application to display correctly.
    </div>
</noscript>
<div style="position: absolute; width: 100%; height: 100%">
    <div style="height: 100px">
    <h1>TreeMap API for JavaScript</h1>
    </div>

    <div style="height: 100%; ">
        <div id="treeMap" style="width: 100%;height: 80%"></div>
    </div>
</div>
</body>
</html>

Data Source

Java/Swing

You may read your data from any data sources such as a database table, a file, a piece of data in memory. The data should be in tabular format that can be converted to TableModel as defined in Java Swing. From TreeMap API point of view, the only data it will accept is the TableModel. As long as you convert your raw data to TableModel, you can use it in TreeMap API.

JavaScript/HTML5

The JavaScript API of TreeMap supports loading data in JSON format or by passing a JavaScript array using one of the method call below:

treeMap.loadJsonUrl("datafile.json");
treeMap.loadJavaScriptArray(javaScriptArray);
treeMap.loadJsonString(jsonString);

When loading data through a URL, please pay attention to the same-origin policy, i.e. the data should originate from the same scheme, hostname, and port number as the TreeMap library.

TreeMap API Architecture

The com.macrofocus.treemap.TreeMap class acts as a façade to the TreeMap API model-view-controller (MVC) architecture. In brief, the controller collects user input, the model manipulates application data, and the view presents results to the user. This class wraps a TreeMapModel, TreeMapView, and TreeMapController interfaces together. It allows easy loading of the data and customization of the most common settings. By default, it uses the following implementations of these interfaces:

DefaultTreeMapModel

The default implementation of the TreeMapModel interface uses a standard Swing TableModel `as the underlying data holder. The default implementation of the `TreeMapModel interface will automatically map the size to the first numerical column, the color to the second numerical column, the labels to the first categorical column, and the group by to the second categorical column.

If your data model is not in tabular form or you want to customize how the data are fed into the TreeMap, then you can extend the AbstractTreeMapModel class. Key methods to implement are getChildren(), getParent() `and `getValueAt().

DefaultTreeMapView

The default implementation of the TreeMapView interface will automatically coalesce multiple changes to the model to a single repaint operation and has the possibility of updating the display progressively should very large datasets be displayed.

DefaultTreeMapController

The default implementation of the TreeMapController interface supports probing, selection, zooming, panning, and drilling operations. Probing is achieved by simply moving the mouse over the shape of interest and a popup will then display detailed information about that item. Selection is done through left-mouse click and lasso operations by pressing down the kbd:[Alt] key while dragging the mouse. Zooming can comfortably be done using a mouse wheel or zooming gesture of a trackpad. Finally, drilling is supported using the Page-Down and Page-Up keys.

TreeMap API Customization

TreeMap API has been designed with customizability in mind. The most common settings can be changed through the TreeMap façade. Should this not be enough, the TreeMapSettings class can be obtained using TreeMap.getModel().getSettings() and provides access to global options, while TreeMapFieldSettings provides access to field-specific options. Default settings can changed using TreeMapSettings.getDefaultFieldSettings() and can be overridden on a field-by-field basis using TreeMapSettings.getFieldSettings(TreeMapField). This provides, for example, with a mean of using different layout algorithm at each hierarchy level.

Configure the size

As the treemap visualization technique attempts to keep the area of the shapes on the screen proportional to some data values, it is key to specify which column in the TableModel should be used as a proxy for the size. This can specified using one of the following two methods, depending on whether you prefer to use the column index or the column name:

treeMap.setSize(2);
treeMap.setSizeByName("Value");

The specified column should contain numerical values of type Integer, Float, or Double. If the value is null, then it will simply not be included in the treemap layout. Since values can be negative but shapes cannot have negative areas, the default behavior is to use the absolute value. To override this behavior, it is possible to use another scaling scheme, such as:

treeMap.setScale(ScaleFactory.getInstance().get("Original"));

In this case, negative values will not be included in the treemap layout.

Assigning colors

How each shape should be colored can be defined by using a colormap that will convert numerical and categorical values into colors according to a defined scale or dictionary. Which column to use should be defined using its index or name:

treeMap.setColor(2);
treeMap.setColorByName("Strength");

Creating colormaps

To override the default colormap that is assigned to each column, it is possible to set a customized one, for example:

treeMap.getModel().getSettings().getFieldSettings(treeMap.getModel().getTreeMapField(2)).setColorMap(ColorMapFactory.getInstance().createSequentialColorMap(0, 100);

The ColorMapFactory class provides a range of static methods for creating standard colormaps for categorical, sequential, and diverging values. The PaletteFactory gives access to a wide range of predefined color gradients.

A Colormap contains both an Interval and a Palettte, which are conjointly used to map the actual values into the normalized range (0..1) used to retrieve the colors defined in the palette. To create a custom colormap and palette that associate -200 to red, 0 to white, and 200 to green, you need to:

 MutableColorMap colorMap = new SimpleColorMap(new ClosedInterval(-200, 400),
                new InterpolatedPalette(new InterpolatedPalette.Entry(0, Color.red),
                new InterpolatedPalette.Entry(0.5, Color.white),
                new InterpolatedPalette.Entry(1, new Color(0, 128, 0)))
        );

You can then customize it further by assigning special colors to values that fall outside of the range defined by the Interval by using the setUnderColor() and setOverColor() methods, or to missing values with the setNullColor() method.

Adding a third dimension

The shapes can extend to the third dimension by specifying the column index or name containing their relative height:

treeMap.setHeight(2);
treeMap.setHeightByName("Strength");

In conjunction, the maximum relative height (as a percentage of the overall treemap size) of the shapes can be controlled through:

treeMap.getModel().getSettings().setMaximumHeight(0.05);

Hierarchical grouping

Treemap visualization is a very effective method when used with hierarchical data. To define how each row of the original TableModel should be grouped and sub-grouped, the list of columns indices or names can be defined:

treeMap.setGroupBy(4, 5);
treeMap.setGroupByByNames("Region", "Department");

Should your data have an unbalanced hierarchy, you can specify the hierarchal placement of each row in the TableModel by having a column of type Path.

Labeling

Labels containing values from the original TableModel can be incorporated within each shape, either as a list where each value is displayed below one another:

treeMap.setLabels(1, 2);
treeMap.setLabelsByName("Value", "Strength");

and/or by filling the entire area with one value:

treeMap.setBackground(0);
treeMap.setBackgroundByName("Name");

Layout Algorithms

TreeMap API includes a wide range of treemap and related layout algorithms:

BINARY_TREE

Uses a static binary tree layout

SLICE

Original slice-and-dice treemap algorithm, which has excellent stability properies but leads to high aspect ratios

SQUARIFIED

the aspect ratio of each rectangle is kept as close as possible to a square

STRIP

An ordered squarified treemap algorithm

PIVOT_BY_SPLIT_SIZE

Pivot by split size

SPLIT

Split layout

CIRCULAR

Circular treemap layout

VORONOI

Voronoi treemap layout

BAR

Bar Char layout

PIE

Pie Chart layout

MATRIX

Matrix Layout

TAG_CLOUD

Tag Cloud layout

The one to use can easily be set using the following method call:

treeMap.setAlgorithm(AlgorithmFactory.SQUARIFIED);

Appearance and Rendering Options

Many options are provided to tune the appearance of the resulting treemap visualization. The most common is to define whether the shapes should be rendered with a cushion effect that highlight the hierarchical placement or using a solid color with a optional border:

treeMap.setRendering(RenderingFactory.CUSHION);
treeMap.setRendering(RenderingFactory. FLAT);
treeMap.setRendering(RenderingFactory. FLAT_NO_BORDER);

Another important customization area is to define how the headers of the various groups be displayed, for example by setting the placement of the header and font to use:

treeMap.setLabeling(LabelingFactory.TOP_LABELING);
treeMap.setHeaderFont(new Font("Tahoma", Font.BOLD, 16));

Similarly, the font to use to display the labels can be specified as well:

treeMap.setLabelingFont(new Font("Tahoma", Font.ITALIC, 18));

Finally, the color used for probing and selection can be customized using:

treeMap.getModel().getSettings().setProbingColor(new Color(200, 200, 0));
treeMap.getModel().getSettings().setSelectionColor(Color.orange);

Customizing textual display

It is possible to fine tune the appearance and the positioning of the headers, labels, and tooltips by customizing their respective renderers: TreeMapHeaderRenderer, TreeMapLabelRenderer, and TreeMapTooltipRenderer. We provide default implementations for each of them that allow to customize, for example, the visual effect (drop shadow, glow), the vertical and horizontal alignment of the text, the minimum number of character to display, and how text should be truncated:

final DefaultTreeMapHeaderRenderer headerRenderer = new DefaultTreeMapHeaderRenderer();

final DefaultTreeMapHeaderRenderer headerRenderer = new DefaultTreeMapHeaderRenderer();
headerRenderer.setEffect(DefaultTreeMapHeaderRenderer.Effect.Glow);
headerRenderer.setRendering(DefaultTreeMapHeaderRenderer.Rendering.Truncate);
headerRenderer.setHorizontalAlignment(DefaultTreeMapLabelRenderer.CENTER);
headerRenderer.setVerticalAlignment(DefaultTreeMapLabelRenderer.TOP);

treeMap.getView().setHeaderRenderer(headerRenderer);

The foreground, background and effect colors, as well as the font, have to be customized through the settings:

treeMap.getModel().getSettings().getDefaultFieldSettings().setHeaderForeground(new Color(156, 156, 156));
treeMap.getModel().getSettings().getDefaultFieldSettings().setHeaderEffectColor(new Color(50, 50, 50));
treeMap.getModel().getSettings().getDefaultFieldSettings().setHeaderBackground(new Color(96, 96, 96));

treeMap.setHeaderFont(new Font("Tahoma", Font.BOLD, 16));

Setting the appropriate Format to each column can specify how values are formatted:

treeMap.getModel().setFormat(3, new DecimalFormat("#,##0.00 $bil"));

Tooltip content customization

The content of the tooltip can be changed by enabling/disabling the display of particular values and labels:

treeMap.getModel().getSettings().setShowPopup(treeMap.getModel().getTreeMapField(7), true);
treeMap.getModel().getSettings().getFieldSettings(treeMap.getModel().getTreeMapField(7)).setShowLabel(true);

In addition, the width of the tooltip and its type can be adjusted. The Painted tooltip type will clip any region outside of the TreeMap component, while the Lightweight type allows for overflow within the application window, and the Heavyweight type relies on a native borderless window that is only limited by the size of the display.

treeMap.getView().getToolTip().setPreferredWidth(190);
treeMap.getView().getToolTip().setType(ToolTipType.Heavyweight);

Furthermore, a tailored implementation of the TreeMapToolTip interface, or an extension of the DefaultTreeMapToolTip can be provided. The Global2000Demo exemplify what can typically be achieved.

Interacting with TreeMap API

Probing and selection

The selected nodes can be retrieved using TreeMapModel.getSelection(). The node currently under the mouse can be accessed using TreeMapModel.getProbing(). It is possible to turn off multiple selection by using the DefaultTreeMapController.setMultipleSelectionEnabled() method.

Extending TreeMap API

Context menu customization

The TreeMapController controls the content of the popup menu. It can be extended with additional entries using:

treemap.getController().getPopupMenu().insert(new JSeparator(), 0);
treemap.getController()getPopupMenu().insert(new AbstractAction("Do something") {
            public void actionPerformed(ActionEvent e) {
                // Place code to do something here
            }
        }, 0);

Handling dynamic data

There is a DefaultTreeMapModel.setTableModel() method that can be used to swap the current TableModel. Best is however to have the table model send appropriate events on data update instead of replacing the entire model, but both are supported. There is a demo (DynamicPortfolioDemo) that exemplifies both approaches. Changing the structure of the data, such as the number of columns, their names and types, is currently not supported.

Scalability

TreeMap API has been designed with performance in mind. Datasets containing 100'000s of data objects can be handled. To assess the effectiveness of the treemap view, one can display timing information using the TreeMapView.setShowTiming(true) method.

Drawing text on screen is an expensive operation and this is the main consideration to take into account when faced with performance issues. Reducing the amount of labels to be displayed can typically solve this. Better yet is the possibility of progressively showing the labels in an iterative or incremental manner and without blocking the user interface. This mode, which is disabled by default, can be enabled using:

treemap.getView().setProgressive(TreeMapView.Progressive.Incremental);

Further speed improvements can be obtained by disabling anti-aliasing, the cushion effect, and the use of the 3rd dimension.

TreePlot

The TreePlot component allows you to create a scatter plot of the data. Any combination of numerical variables can be used to map to the x- and y-axes as well as size and color of the glyphs. The TreePlot component follows the same architecture as the TreeMap API component, and can instantiated with a TreeMapModel to have the TreeMap and the TreePlot synchronized, or with a TableModel to use it independently.

Migrating from JIDE TreeMap

The migration from JIDE TreeMap should be straightforward. Simply change the package names from com.jidesoft.treemap, com.jidesoft.colormap, and com.jidesoft.palette to com.macrofocus and you should be all set.

What’s Next

There are still many features that we want to add to this product but haven’t got a chance to do so.

  1. Provide demo to demonstrate scalability of TreeMap API.

  2. Improve documentation for creating custom colormaps.

  3. Include TreeTable wrapper.

  4. Show how to encode Path information for single cell value.

  5. Provide support for animated transitions.

If you have any feedbacks and suggestions, please feel free to email us.