It has always been a challenge to draw objects in GWT because SVG and VML are not supported out of the box. In a modern web application, it is useful to have charts or other richly interactive visuals, particularly in a data heavy application where it is important to visualize relationships and ideas.
At the moment, Ext GWT 2.0 provides this functionality, but it is dependent on the third party Flash library, Open Flash Chart. While this library met the need for visualizations, there was a lack of extensibility and customization. In Ext GWT 3.0 we solve this problem by providing a draw framework that runs everywhere, from IE6 to Chrome 12 that is developed as a pure GWT library.
Table of Contents
Draw Engine
Ext GWT 3.0 makes all of this possible with a new drawing system that chooses the proper rendering engine for the browser. The Draw package uses an abstract class called Surface that is replaced during compilation by the appropriate draw engine for the user agent. The SVG engine is used by default for the majority of browsers, and the VML engine is used for IE 6, 7, and 8. This all takes place seamlessly behind the scenes, allowing visual parity between all browsers.
<replace-with class='com.sencha.gxt.chart.client.draw.engine.SVG'> <when-type-is class='com.sencha.gxt.chart.client.draw.Surface' /> </replace-with> <replace-with class='com.sencha.gxt.chart.client.draw.engine.VML'> <when-type-is class='com.sencha.gxt.chart.client.draw.Surface' /> <any> <when-property-is name='user.agent' value='ie6' /> <when-property-is name='user.agent' value='ie8' /> </any> </replace-with>
Draw Sprites
The root of the draw package is DrawComponent. DrawComponent is a component that provides a canvas-like area for visual elements. The scene of a DrawComponent is made up of sprites from the various types of geometric primitives rendered by the surface engine, ranging from ellipses to text.
DrawComponent component = new DrawComponent(600, 400);
Gradients are supported and can be used in place of any color attribute.
CircleSprite circle = new CircleSprite(); circle.setCenterX(30); circle.setCenterY(100); circle.setRadius(25); Scaling scale = new Scaling(); scale.setX(2); scale.setY(2); circle.setScaling(scale); circle.setStroke(new Color("#999")); Gradient gradient = new Gradient("gradient", 21); gradient.addStop(0, new Color("#79A933")); gradient.addStop(13, new Color("#70A333")); gradient.addStop(34, new Color("#559332")); gradient.addStop(58, new Color("#277B2F")); gradient.addStop(86, new Color("#005F27")); gradient.addStop(100, new Color("#005020")); circle.setFill(gradient); component.addGradient(gradient); circle.setStrokeWidth(3); component.add(circle);
The PathSprite is the lowest level sprite element, and it allows the developer to manually set up the vector elements of a sprite. We have made the path system easier by using strongly typed PathCommands. PathSprite also provides useful helper functions such as line smoothing.
PathSprite path = new PathSprite(); path.addCommand(new MoveTo(75, 75)); path.addCommand(new CurveTo(0, -25, 50, 25, 50, 0, true)); path.addCommand(new CurveTo(0, -25, -50, 25, -50, 0, true)); path.setStroke(new Color("#000")); path.setStrokeWidth(2); path.setFill(new Color("#fc0")); path.setFillOpacity(0.25); component.add(path);
Chart Example
To better understand the new charting system, let’s walk through one of the examples.
Store
The data used in the chart is accessed from a store. In this example, ValueProviders are used to link the data to corresponding areas of the chart. A ValueProvider instance takes a model object and returns a value. Each Series is designed to accept a certain type of value and will only accept ValueProviders that supply that type. While you can implement your own ValueProvider instances, if your data model has getters and setters, you can define a PropertyAccess type to supply ValueProvider instances as needed. In this example, we are using the Movies class, which has several properties of type double, each with its own getter and setter. Each genre represents the number of releases for the given year.
public interface MoviesPropertyAccess extends PropertyAccess<Movies> { ValueProvider<Movies, Double> action(); ValueProvider<Movies, Double> comedy(); ValueProvider<Movies, Double> drama(); ValueProvider<Movies, Double> thriller(); ValueProvider<Movies, Integer> year(); ModelKeyProvider<Movies> yearKey(); } private static final MoviesPropertyAccess moviesAccess = GWT.create(MoviesPropertyAccess.class); @Override public Widget asWidget() { final ListStore<Movies> store = new ListStore<Movies>(moviesAccess.yearKey()); store.addAll(TestData.getMovieData(2005, 4, 0, 10000)); final Chart<Movies> chart = new Chart<Movies>(600, 400); chart.setStore(store); chart.setChartShadow(true); chart.setAnimated(true);
Axis
Axes come in various types and are added to the chart on which they will appear. Only one axis can be used for each position on the chart. You then link ValueProviders from the store to provide the data sets that are used by the axis. This example also shows the configuration for the title, grid, and subtick steps. The NumericAxis will interpolate between the values in the store whereas the CategoryAxis will only use values in the store. Some charts require a special Axis that is unique to that chart – such as with Gauge and Radar, for example.
NumericAxis<Movies> axis = new NumericAxis<Movies>(); axis.setPosition(Position.BOTTOM); axis.addField(moviesAccess.comedy()); axis.addField(moviesAccess.action()); axis.addField(moviesAccess.drama()); axis.addField(moviesAccess.thriller()); axis.setDisplayGrid(true); chart.addAxis(axis); CategoryAxis<Movies> catAxis = new CategoryAxis<Movies>(); catAxis.setPosition(Position.LEFT); catAxis.addField(moviesAccess.year()); chart.addAxis(catAxis);
Legend
The Legend provides a key of the data represented in the chart. The legend adapts to the series added to the chart and uses items that pertain to the type of data set represented in that series. A legend is an easy way for the end user to highlight or hide data in the chart.
Legend<Movies> legend = new Legend<Movies>(); legend.setPosition(Position.RIGHT); chart.setLegend(legend);
Series
A Series functions in a similar way to an Axis. Each series represents a different chart type, such as Pie or Radar. First, you add ValueProviders for accessing the data represented in the Series. Next, you add the colors associated with the Series.
BarSeries<Movies> bar = new BarSeries<Movies>(); bar.setyAxisPosition(Position.BOTTOM); bar.addyField(moviesAccess.comedy()); bar.addyField(moviesAccess.action()); bar.addyField(moviesAccess.drama()); bar.addyField(moviesAccess.thriller()); bar.addColor(new RGB(148, 174, 10)); bar.addColor(new RGB(17, 95, 166)); bar.addColor(new RGB(166, 17, 32)); bar.addColor(new RGB(255, 136, 9)); bar.setStacked(true); chart.addSeries(bar);
The advantage of separating the chart types into series, instead of making them a part of the chart itself, is that various chart types can be mixed together. In the example below, a Scatter, Line, and Bar Series are all used on the same chart.
Chart Types
Next, let’s run through all of the chart types in this preview and give a general overview of how they are used.
The stacked Area Chart is useful when displaying multiple aggregated layers of information.
A Bar Chart is a useful visualization technique to display quantitative information for different categories that shows some progression (or regression) in the data set.
Gauge Charts are used to show progress in a certain variable.
A Line Chart is a useful visualization technique to display quantitative information for different categories or other real values (as opposed to the bar series), that can show some progression (or regression) in the data set.
A Pie Chart is a useful visualization technique to display quantitative information for different categories that also have a meaning as a whole.
A Radar Chart is a useful visualization technique for comparing different quantitative values for a constrained number of categories.
The scatter plot is useful when trying to display more than two variables in the same visualization.
Summary
Be sure to try all of the examples—and in particular, the legend interactivity. Keep in mind that this is still preview, so expect additional features as well as other improvements. This is just scratching the surface of what is possible with the new Draw and Charting framework. We can’t wait to see what you do with them.
Looks great – any reason you are not leveraging GWT Visualization? (http://code.google.com/p/gwt-google-apis/wiki/VisualizationGettingStarted). It seems like Google is pushing the Visualization library for users of GWT and so I am just curious to see why you would implement something different. Just curious. Thanks and can’t wait for GXT 3.0.
I have played with the preview release and I liked it. It is a big improvement over 2.x. But I do have complaints about the following 2 aspects of the design:
1. Tying legend to series
Sometimes you want legend even with 1 series. With this design, it is impossible to accomplish this.
2. Tying colors to series (Column Chart)
This is related to #1. If you have a single series of data, the chart will be more appealing if you have a different color for each column. But with this design, I don’t know if it can be done at all.
Some of these methods don’t seem to conform to Java naming standards:
bar.setyAxisPosition
bar.addyField
@Vinny Google vis requires an external connection to Google, which many of my clients won’t tolerate.
Hi, please think about using logarithmic scale on y-axis and maybe more y-axis beyond the chart, some on left-side and some on right-side.
Is there any drag and drop implementation planed for sprites – or can you point me at a good resource to implements it myself?
Looks very good.
Is there going to be any support for exporting the charts to clipboard / saving to local machine or printing them?
@Vinny
That wraps a Js library, which would defeat the purpose of having a pure GWT based framework.
@Henry Sue
1. I’ll look into adding that functionality.
2. In the third preview release Bar Renderer Example shows how to do this.
@The Jackal
Thanks for pointing that out, result of eclipse generated getters and setters. It was changed for the third preview release.
@Haixu Huang
That could be accomplished by manipulating data on the store side.
@Mathias
That is a planned feature for beta1.
@Omar
That was discussed in the first developer preview. https://www.sencha.com/blog/ext-gwt-3-dev-preview-1/ Short answer: that can be accomplished server side, but not client side.
Are there any plans to add Canvas support for drawing? We make heavy use of ExtJs graphs in our application, however, it’s performance isn’t that great given that it uses SVG as its drawing engine.
With Canvas, the performance would be greatly improved over SVG.
@Daniel
We do plan to add Canvas support, however not in the scope of the initial 3.0 release. One of the strengths of the framework is that the drawing logic is completely abstracted from the rendering engine. This will allow us to add a Canvas engine soon after.
Looks great!
Is the charting framework going to support markers?
@Arkady
Not sure what you mean by markers. Charts like Line and Radar do have sprite markers to represent its points. Also there will be Label and Tooltip support in preview release 4.
By markers I mean the ability to generate a pointer with a label to a particular point on a line chart.
Kudos to you! I hadn’t toughht of that!
You mean I don’t have to pay for expert avidce like this anymore?!
Great work guys. This looks like a good design that will save us some time!
I am in accordance completely!