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.
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.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.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.treemap.TreeMap.setLicenseKey("My Company", "ABC12-ABC12-ABC12-ABC12-ABC12-ABC12");
treeMap = new com.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.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.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.
-
Provide demo to demonstrate scalability of TreeMap API.
-
Improve documentation for creating custom colormaps.
-
Include
TreeTable
wrapper. -
Show how to encode
Path
information for single cell value. -
Provide support for animated transitions.
If you have any feedbacks and suggestions, please feel free to email us.