GraphViz diagrams from Visualforce

The Google Chart API returns a dynamically generated chart image in response to a GET or POST request and articles like Integrating Visualforce and Google Charts provide examples of how to use the API from Force.com. I had a need to represent a graph of relationships and noticed that GraphViz is available (but marked as experimental) through the API. GraphViz is used by SchemaSpy to automatically layout the diagrams of the relationships between objects – see SchemaSpy Org ERD for an example for a Force.com org.

Here is an example of the result of using this API to represent a set of pages and the links between them:

The code to create this output is very simple as the code fragment below illustrates. All you have to do is concatenate together the string that you have worked out using the documentation and the Live Chart Playground. But beware of the 2k length limitation of the resulting encoded URL when using the normal GET. This severely limits how many elements the diagram can include (to about 30 pages for my example even after tweaking to minimize the length). I could not find a way staying solely in Force.com to move to using a POST where the length limit is 16k.

private String createUrl(QuestionPage__c[] qps, AnswerOption__c[] aos) {
    // See http://code.google.com/apis/chart/docs/making_charts.html
    String url = 'http://chart.apis.google.com/chart'
            + '?cht=gv&chl=digraph{[bgcolor=gray97]node[shape=note,style=filled,fillcolor=lightblue,fontsize=9]';
    // Translate ids into indexes - shorter
    Map<Id, Integer> m = new Map<Id, Integer>();
    Integer i = 0;
    for (QuestionPage__c qp : qps) {
        m.put(qp.Id, i);
        i++;
    }
    // Ensure all the pages, whether related or not are shown; just use name as title field is much longer
    for (QuestionPage__c qp : qps) {
        url += m.get(qp.Id) + '[label="' + qp.Name + '"]';
    }
    // Show page to page links - solid lines
    for (QuestionPage__c qp : qps) {
        if (qp.NextPage__c != null) {
            url += m.get(qp.Id) + '->' + m.get(qp.NextPage__c) + ';';
        }
    }
    // Show links via answer options - dashed lines
    url += 'edge[style=dashed]';
    for (AnswerOption__c ao : aos) { 
        if (ao.Question__r.QuestionPage__r.Id != null && ao.NextPage__c != null) {
            url += m.get(ao.Question__r.QuestionPage__r.Id) + '->' + m.get(ao.NextPage__c) + ';';
        }
    }
    url += '}';
    return url;
}

The Visualforce to display the image is simply <apex:image url=”{!url}” />.

Advertisements

4 thoughts on “GraphViz diagrams from Visualforce

  1. To perform a POST while staying within Force.com, you could just call your class method from JavaScript generating the POST with dynamic HTML. In this scenario, you’ll need to return your Google post parameters to the VF page instead of the url (below variable listed as GoogleParams)…the core url passed in a separate method or coded static in the form of the DHTML (as done below). Instead of using an image tag, display the results in an iFrame…like such:

    ———————–

    {
    var HTMLstring = ”;
    HTMLstring += ”;
    HTMLstring += ”;
    HTMLstring += ”;
    HTMLstring += ”;

    var newdocument=window.frames[‘MyiFrame’].document;
    newdocument.write(HTMLstring);
    newdocument.close();
    window.frames[‘MyiFrame’].document.getElementById(‘myForm’).submit();
    ———————–

  2. Looks like my code didn’t render correct. Let’s try this HTML encoded:

    ——————–
    <apex:page>

    <iframe height="100%" width="100%" id="MyiFrame" name="MyiFrame" />
    <script type="text/javascript">
    {
    var HTMLstring = '<html><head></head><body>'; HTMLstring += '<form id="myForm" method="post" action="http://chart.apis.google.com/chart">&#039;; HTMLstring += '<input type=hidden name="cht" value="gv">'; HTMLstring += '<input type=hidden name="chl" value="{!GoogleParams}">'; HTMLstring += '</form></body></html>';

    var newdocument=window.frames['MyiFrame'].document;
    newdocument.write(HTMLstring);
    newdocument.close();
    window.frames['MyiFrame'].document.getElementById('myForm').submit();
    }
    </script>
    </apex:page>
    ——————–

  3. Using the above IFRAME approach I could not get automatic height sizing to work either using styles or JavaScript (because the content of the IFRAME is from a different domain in this case) so ended up just fixing the height to 500px and living with the scrollbar. A further problem is that when the user tries to save the image a fragment of “Bad Request” HTML gets saved instead of the image data.

    I wish Visualforce/Apex included the mechanisms that pretty much every other framework has to allow a server-side component accessed via a GET from the apex:image tag to do the POST and return the image data…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s