Suggestion for new type of ADF DVT (Data Visualization): the Delta Graph – to visualize relative changes integrated in a table layout

0

I may have bored you before with stories about Data Visualization. It is one of my favorite topics. We deal in data. And visualization of data can help to increase the value of the data tremendously. Proper visualization provides quicker insight and reveals the true meaning of the numbers in an instant.

Newspapers frequently use graphics to illustrate the news reported in their articles. My morning paper has a broad palette of ways to represent numbers, trends, aggregates and incidents. It inspires me to mimic in my own toolset: ADF.

The other day, my newspaper printed the next figure that illustrates the changes in circulation for all Dutch newspapers – comparing the 1st quarter of 2011 with one year ago.

Image

I quite like this presentation. It reveals a lot of information in an appealing way. I started wondering it this way of presenting changes would be easy to implement in ADF Faces applications. My first port of call obviously was the ADF DVT (Data Visualization Tags) library. However, it did not seem to offer a graph type that is very close to this presentation. Gauges appeared to come closest, but not quite there. And the inline-display inside table rows is related to spark charts, but again, it is not quite the same.

So I started playing with ‘ordinary’ ADF Faces and – using some CSS definitions – I came up with the following ADF Faces rendition of the Delta Graph:

Image

In addition to the initial presentation, this ADF based version of the Delta Graph allows manipulation of the view, for example sorting the records by change or by current circulation:

Image

Image

Before creating the – data bound – Delta Graph display, I needed to create a data set that I could work with and bind the table to. The easiest way of doing this was through the use of a Placeholder Data Control – that allows we to work with data that to the application appear like data from an enterprise resource such as a web service or a database but that in fact is typed in or read from a CSV file.

Next I created the web page with data bound table – based on the circulation records from the placeholder data control. Then, the essential step, I manipulated CSS properties until I achieved the desired effect.

Creating the NewsPaperCirculation Placeholder Data Control

Creating a Placeholder Data Control is done from the New Gallery.

Image

The configuration wizard appears, asking for a name for the Data Control. The next step is the definition of the Placeholder Data Type(s).

Image

Provide the name of the Data Type and the names and types of the attributes. For each attribute, UI properties such as Label and Format can be specified.

Image

Switch to the Sample Data tab to register the data that this ‘dummy’ data control will feed into the ADF Binding framework. This data can be typed in or read from a source file.

Image

Close the wizard with the OK button.

The Data Control is now available in the Data Control Palette:

Image

Create the Web Page with Data Bound table

Create a new JSF Web Page in the normal way, from the New Gallery:

Image

The page opens in the visual editor. Now, drag and drop the NewsPaperCirculation collection from the Placeholder Data Control to the page – drop it as an ADF Read Only Table:

Image

Configure the table in a straightforward way – enabling sorting of the records in the table:

Image

Press OK to complete the wizard and create the data bound table in the page.

Image

At this point, you can run the page and you will see the data from the placeholder data control presented in the web page in a rich ADF Faces table.

Create the Delta Graph display

The Delta Graph display relies on a combination of ADF Faces components and their CSS properties. In addition, there are two methods used on a managed bean – one to answer the question whether or not a delta-value (an instance of oracle.jbo.domain.Number) is above zero and one to calculate the width of the bar – given the delta value, the maximum width and the maximum delta value. I have tried here to make use of the EL 2.2 specification that allows use of parameters and custom functions in EL expressions. However, I could not get this to work with WebLogic 10.3.x and JDeveloper 11gR2. The OTN Forum and other websites suggested similar problems as the ones I ran into – but none of the suggested solutions helped me out. I have used a somewhat awkward approach to calling custom functions from EL expressions – a workaround using a Map implementation; it does the job, but I am sure today there are better ways to doing this.

The snippet in the JSF page that is used for creating the delta-bar is constructed like this:

 <af:column sortProperty="#{bindings.NewsPaperCirculation.hints.Delta.name}" sortable="true"
            headerText="Change in %" id="c2">
     <af:panelGroupLayout id="graphPGL" layout="horizontal">
         <af:panelGroupLayout id="graphPGLNeg" layout="horizontal" inlineStyle="width:100px;text-align:right;">
             <af:outputText value="#{row.Delta}" id="ot2"
                            inlineStyle="padding-right:5px;font-weight:bold;"
                            rendered="#{!myInvokableMap.init[row.Delta].invoke.aboveZero}">
             </af:outputText>
             <af:outputText value=" " id="deltaMin" clientComponent="true"
                            inlineStyle="padding-left:#{myInvokableMap.init[100][10][row.Delta].invoke.width}px;background-color:red"
                            rendered="#{!myInvokableMap.init[row.Delta].invoke.aboveZero}"/>
         </af:panelGroupLayout>
         <af:panelGroupLayout id="graphPGLPos" layout="horizontal" inlineStyle="width:100px;">
             <af:outputText value=" " id="deltaPlus" clientComponent="true"
                            inlineStyle="padding-right:#{myInvokableMap.init[100][10][row.Delta].invoke.width}px;
                            background-color:blue;"
                            rendered="#{myInvokableMap.init[row.Delta].invoke.aboveZero}"/>
         <af:outputText value="+ #{row.Delta}" id="ot2b"
                        inlineStyle="padding-left:5px; font-weight:bold;"
                        rendered="#{myInvokableMap.init[row.Delta].invoke.aboveZero}">
         </af:outputText>
         </af:panelGroupLayout>
     </af:panelGroupLayout>
 </af:column>

It makes use of the managed bean MyInvokableMap that responds to the commands aboveZero and width. The salient part of the implementation of this bean is shown here:

@ManagedBean(name = "myInvokableMap")
public class MyInvokableMap implements Map {

    static final int SETTING_PARAMETERS = 1;
    static final int INVOKING = 2;

    int mode = SETTING_PARAMETERS;

    List<Object> params = new ArrayList<Object>();

    public Object get(Object key) {
        boolean isString = key instanceof String;
        // if the init is passed, this object is reinitialized for the next invocation
        if (isString && ((String)key).equalsIgnoreCase("init")) {
            mode = SETTING_PARAMETERS;
            params.clear();
            return this;
        }
        // if set is passed, we can expect the next call to get to pass the value of the next attribute
        if (isString && ((String)key).equalsIgnoreCase("set")) {
            mode = SETTING_PARAMETERS;
            return this;
        }
        // if invoke is passed, the next call to get will pass the key that optionally identified the method to invoke; we switch now from SETTING_PARAMETERS mode to INVOKING mode
        if (isString && ((String)key).equalsIgnoreCase("invoke")) {
            mode = INVOKING;
            return this;
        }
        if (mode == SETTING_PARAMETERS) {
            params.add(key); // store this parameter with the current number of entries as the key; that means that parameters are stored in params with keys 0..x where x is one less than the number of parameters
            return this;
        }
        if (mode ==
            INVOKING) { // now depending on the value of key, we can decide to invoke the method that is called for

            if (((String)key).equalsIgnoreCase("aboveZero")) {
                return ((oracle.jbo.domain.Number)params.get(0)).compareTo(0) > 0;
            } else if (((String)key).equalsIgnoreCase("width")) {
                Long maxWidth = (Long)params.get(0);
                Long maxValue = (Long)params.get(1);
                oracle.jbo.domain.Number actualValue = ((oracle.jbo.domain.Number)params.get(2));
                double w = actualValue.abs().floatValue() / maxValue;
                Long width = Double.valueOf(w * maxWidth).longValue();
                return width;
            } else if (((String)key).equalsIgnoreCase("command")) {
                // Execute command, whatever it is
                return null;
            }
        }
        return null;
    }

Running the page results in

Image

which through sorting can be displayed as:

Image

and many other ways.

I hope that this article provides a starting point for experimenting with this type of Data Visualization. Perhaps the Oracle ADF DVT team could consider creating something in their vast collection of data visualizations – or perhaps point me in the direction of an existing component that can do something like this.

Resources

Download the JDeveloper 11gR2 application with the sources from this article: DeltaGraphAndODTUG_Dashboarding.

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

Leave a Reply