Graph processing and visualization in Cuba?

We need to visualize graphs extracted from our Cuba application.
Is there any support for integrating graph visualization libraries (such as KeyLines, D3 etc)?
What is the best approach to do this?

Thank you!

Hi,

There you go:

Bye
Mario

I am aware of Cubaā€™s Chart and Maps add-on.
My question is in regards to graph components, as in vertices and edges, graph traversals etcā€¦
D3 and KeyLines are good examples.

I need to interface my Cuba application with graph processing libraries.

Hi!

You can add any JS library and wrap it by Vaadin component. See documentation how to do that.
Iā€™ve created a simple project that uses D3.js. It sends JSON with data to the client side and builds a small diagram about the progress of tasks.

You should add all JS scripts that you need and CSS styles like in code below:

@JavaScript({"d3component-connector.js", "vaadin://d3.min.js"})
@StyleSheet("vaadin://diagram.css")
public class D3Component extends AbstractJavaScriptComponent {

Then in our JS connector we can use D3.js:

com_company_simpled3_web_toolkit_ui_d3component_D3Component = function() {
    var connector = this;
    var element = connector.getElement();
    var dataJson = [];

    connector.onStateChange = function(){
        dataJson = JSON.parse(connector.getState().dataJson);
        drawDiagram(element);
    };

    function drawDiagram(element) {
        if (dataJson.length === 0) {
            return;
        }

        d3.select(element).selectAll('div.item').data(dataJson).enter()
            .append('div').attr('class', 'item')
            .append('div').attr('class', 'data').append('span');

        d3.select(element).selectAll('div.item').data(dataJson)
            .select('div').style('width', function (d) { return (d.progress * 3) + 'px';})
            .select('span').text(function(d){return d.progress;});

        d3.select(element).selectAll('div.item').data(dataJson)
            .append('div').attr('class', 'name')
                          .text(function (d) {return d.name;});

        d3.select(element).selectAll('div.item').data(dataJson).exit().remove();
    }
};

And finally we add our component to the layout. Task class is a POJO.

@Inject
private VBoxLayout vbox;

@Override
public void init(Map<String, Object> params) {
    List<Task> tasks = Arrays.asList(
            new Task("Task1", 14),
            new Task("Task2", 26),
            new Task("Task3", 82),
            new Task("Task4", 57));

    D3Component d3Component = new D3Component();
    d3Component.setDataJson(new Gson().toJson(tasks));

    Layout layout = vbox.unwrap(Layout.class);
    layout.addComponent(d3Component);
}

And we get the following result:

image

Attached project: simple-d3.zip (158.0 KB)

2 Likes

Hi Pinyazhin,

I am trying to add a simple example with google table query js library similar to the process as mentioned in your comment.

I edited JS connector and added the JS script to the component class but I donā€™t see any results as expected. Do I have to make changes in the other files as well?
JS Connector:

com_company_simpled3_web_toolkit_ui_d3component_D3Component = function() {
    var connector = this;
    var element = connector.getElement();
    var dataJson = [];
    var dataSourceUrl = 'https://spreadsheets.google.com/tq?key=rh_6pF1K_XsruwVr_doofvw&pub=1';
    var query, options, container;

    connector.onStateChange = function(){
        //dataJson = JSON.parse(connector.getState().dataJson);
        //drawDiagram(element);
        init();
    };

    function init() {
            query = new google.visualization.Query(dataSourceUrl);
            container = document.getElementById("table");
            options = {'pageSize': 5};
            sendAndDraw();
      }

    function sendAndDraw() {
            query.abort();
            var tableQueryWrapper = new TableQueryWrapper(query, container, options);
            tableQueryWrapper.sendAndDraw();

    }};

Regards,
Sanchit

HI!

Do you see exceptions in DevTools of your browser or in Tomcat logs? Make sure that you use all necessary js files.

I do see an exception:

SEVERE: Error sending state change eventscom.google.gwt.event.shared.UmbrellaException: Exception caught: (ReferenceError) : google is not defined

What do I have to add and where?

Thanks.

Regards,
Sanchit

It seems that the Google library is not imported. DId you add js files to the vaddin folder in web module? If it is possible could you attach a sample project where the problem is reproduced?

@Pinyazhin I did add the js files to the ui toolkit web module but do I have to add dependencies in the build.gradle web module?

sample.tar.gz (396.1 KB)

No, you shouldnā€™t add any dependencies to the build.gradle. Following the example in google docs you need to add loader.js to your OrgChartComponent:

@JavaScript({
        "vaadin://tablequerywrapper.js",
        "https://www.gstatic.com/charts/loader.js",
        "analytics-connector.js"
})

And you can load it from connector.onStateChange:

connector.onStateChange = function(){
    google.charts.load('current', {'packages' : ['table']});
    google.charts.setOnLoadCallback(init);
};

I reworked your sample project with above code: orgchart.zip (42.9 KB)

1 Like

@Pinyazhin Thanks I will give it a try!

@Pinyazhin It worked! Thank you.

I was just wondering If I can do the same by overriding a component action. for example, the chart should appear on a button click action.
It would be a great help if you could provide me an example.

Sanchit.

You can just add your component to the layout by click on a button:

<button id="showChartBtn" caption="Show chart" invoke="showChart"/>

@Inject
private ComponentsFactory factory;

public void showChart() {
    MyComponent component = (MyComponent) factory.createComponent(MyComponent.class);
    add(component);
}

Or for framework v7:

<button id="showBtn" caption="Show"/>

@Inject
private UiComponents uiComponents;

@Subscribe("showBtn")
private void onShowBtnClick(Button.ClickEvent event) {
    MyComponent component = uiComponents.create(MyComponent.class);
    getWindow().add(component);
}
1 Like

@Pinyazhin I have a really annoying error when I just define a variable in the JS connector from Google API.

var formatter = new google.visualization.NumberFormat({prefix: ā€˜$ā€™, negativeColor: ā€˜redā€™, negativeParens: true});

Exception Trace:

com.vaadin.client.communication.MessageHandler
SEVERE: Error sending state change eventscom.google.gwt.event.shared.UmbrellaException: Exception caught: (TypeError) : Cannot read property 'NumberFormat' of undefined
	at Unknown.Ig(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.Mg(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.Sg(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.ry(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.new uy(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.Rx(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.VOb(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.hPb(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.AUc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.uUc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.JUc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.IGc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown._Hc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.cIc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.$Mc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.mNc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.eval(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.qi(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.ti(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.eval(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
Caused by: com.google.gwt.core.client.JavaScriptException: (TypeError) : Cannot read property 'NumberFormat' of undefined
	at Unknown.Zqb(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.$x(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.Rx(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.VOb(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.hPb(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.AUc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.uUc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.JUc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.IGc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown._Hc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.cIc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.$Mc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.mNc(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.eval(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.qi(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.ti(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)
	at Unknown.eval(com.haulmont.cuba.web.toolkit.ui.WidgetSet-0.js)

Could you please check what is causing this issue.

This is concerning the library you use. You can check Google documentation in order to know which dependency you need to add for:

google.visualization.NumberFormat

Hi,
i am trying to use the simple-d3 project provided here. I am using cuba platform version 7.2.5. As the program do not run in this platform and as AbstractWindow is shown deprecated. I have made few changes as follows;

  • Screen is extended using Screen in place of AbstractWindow
  • In controller, instead of using function public void init(Map<String, Object> params) have used regular public void onInit(InitEvent event) function.

I am getting an error that ā€œno init function is defined and define one of following giving four optionsā€ though com_company_trial3d1_web_toolkit_ui_d3component_D3Component is already defined in js file. Though rest of the code and files are same as provided in your project.
Please guide. Thanks

Hi,
possibly the previous version of your js connector is cached in the browser. You can try to open your app in private / incognito mode to check. Also, the connector function name:

com_company_trial3d1_web_toolkit_ui_d3component_D3Component

should be the same as FQN of D3Component class:

com.company.trial3d1.web.toolkit.ui.d3component.D3Component

Thanks Roman for your reply,
I have tried as suggested by you but the error persists. I am attaching the project. Please have a look & guide.
Thanks
trial3d1.zip (604.1 KB)

Take a look at D3Component class. In @JavaScript you have:

d3component-connector.js

but it should be:

D3Component-connector.js

Also, in D3Component-connector.js replace: getJson with dataJson. It should be like this:

dataJson = JSON.parse(connector.getState().dataJson);

Because component state does not have getJson field. If task names are misaligned, try to replace position: fixed; with position: absolute; in ā€œ.nameā€ selector.

Thanks Roman,
@Javascript connector Name has solved the problem.
Thanks again.