Search

SVG graphs via XSLT on eXist

XSLT for SVG generation

Dave Pawson has anglicised some great XSLT work from Jakub Vojtíšek and provided Relax NG schemas and other improvments. These stylesheets convert from an XML graph specification to SVG.   These would be very useful in XML database work where the graph data is generated on the fly.

I loaded Dave's versions of the spreadsheets and some of the examples in his article into eXist..  There are scripts to graph one or multi-dimensional data and XY plots. eXist has a transform module which allows a XSLT stylesheet to be applied to a selected XML node.  Parameters are limited to string values, so the stylesheet template cannot be passed the graph definition.  The supplied scripts include a linking stylesheet but I could  not get the xsl:include to work on eXist (1.4) (relative or absolute URI's). This probably my fault rather than a bug, so I had to merge the two to get a working XSLT script. 

Here is a simple  XQuery script to transform a stored example, choosing the appropriate stylesheet to use:



declare namespace gr = "http://graph2svg.googlecode.com";
declare option exist:serialize "method=xml media-type=image/svg+xml";

let $example := request:get-parameter("example",())
let $graph := doc(concat("/db/Wiki/graph2svg/",$example))/*
let $ss := 
      typeswitch ($graph)
       case element(gr:osgr)   return   doc("/db/Wiki/graph2svg/xosgr2svg.xsl")
       case element(gr:msgr) return  doc("/db/Wiki/graph2svg/xmsgr2svg.xsl")
      case element(gr:xygr)     return  doc("/db/Wiki/graph2svg/xxygr2svg.xsl")
      default return ()
return 
   transform:transform ($graph,$ss,())

OSGR - One Series Graph: Pie Charts, Bar charts, Line Charts - XSLT     RNG

A Bar chart:  graph spec  svg

A Pie Chart:  graph spec  svg

MSGR - Multiple Series Graph: Bar Charts, Box charts..  XSLT   RNG

A Multi-line charts: graph specsvg

A Box char: graph spec   svg

XYGR - X-Y plots   XSLT  RNG

Two curves: graph spec   svg

SVG in XHTML

I really want to generate the SVG on the fly and include in an HTML document. After much wasted time re-learning that the media-type has to be application/xhtml+xml, this script embeds the generated chart, in a suitably sized area:



declare namespace gr = "http://graph2svg.googlecode.com";
declare option exist:serialize "method=xhtml media-type=application/xhtml+xml omit-xml-declaration=no indent=yes 
        doctype-public=-//W3C//DTD XHTML 1.0 Strict//EN
        doctype-system=http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";

let $example := request:get-parameter("example",())
let $graph := doc(concat("/db/Wiki/graph2svg/",$example))/*
let $ss := 
      typeswitch ($graph)
       case element(gr:osgr)   return   doc("/db/Wiki/graph2svg/xosgr2svg.xsl")
       case element(gr:msgr) return  doc("/db/Wiki/graph2svg/xmsgr2svg.xsl")
       case element(gr:xygr)     return  doc("/db/Wiki/graph2svg/xxygr2svg.xsl")
      default return ()
let $svg :=    transform:transform ($graph,$ss,())
return
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:svg="http://www.w3.org/2000/svg"
      xml:lang="en">
  <head>
    <title>SVG embedded inline in XHTML</title>
    </head>
  <body>
     <h1> SVG embedded inline in XHTML : {$example}</h1>
     <svg:svg width="500px" height="400px">
        {$svg}
     </svg:svg>
  </body>
</html>


A Pie Chart:    svg

Generating SVG Graphs

The data on fires in the UK, gathered by converting an Excel spreadsheet (discussed in a previous item) could be transformed to an XML graph spec and hence to SVG embedded in HTML.  Here is the code:



declare namespace d= "http://www.cems.uwe.ac.uk/xmlwiki/data" ;

declare option exist:serialize "method=xhtml media-type=application/xhtml+xml omit-xml-declaration=no indent=yes 
        doctype-public=-//W3C//DTD&#160;XHTML&#160;1.0&#160;Strict//EN
        doctype-system=http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
let  $fires := doc("/db/Wiki/Convert/fires.xml")//d:fires

let $graph := 
<msgr xmlns="http://graph2svg.googlecode.com" pointType="circle"  colorScheme="cold">
<title>Fires in UK</title>
<names>
{for $year in (2001 to 2008)
 for $qtr in (1 to 4)
 return
      <name>{concat("'",substring(xs:string($year),3,2),"/",$qtr)}</name>
   }
</names>
{for $category in ("dwellings", "otherBuildings","vehicles")
 return
 <values>
    <title>{$category}</title>
    {  for $year in (2001 to 2008)
       for $qtr in (1 to 4) 
       for $value in $fires[@year=$year][@qtr=$qtr][@category=$category]
    return
       <value>{string($value)}</value>
    }
 </values>
 }
</msgr>

return 
<html xmlns="http://www.w3.org/1999/xhtml" 
      xmlns:svg="http://www.w3.org/2000/svg">
  <head>
    <title>Fires in the UK</title>
  </head>
  <body>
    <h1>Fires in the UK</h1>
     source
     {transform:transform( $graph, doc("/db/Wiki/graph2svg/xmsgr2svg.xsl")  ,()   )}
  </body>
</html>


This script is running from a cached XML file, but we can do it on the fly directly from the Excel file by changing the source of the data to call the conversion script:

let  $fires := doc("http://www.cems.uwe.ac.uk/xmlwiki/Convert/fires-ns.xq")//d:fires

This looks like a good case for using an XProc pipeline instead of these ad-hoc connections.

Thanks Dave for bringing Jakub's work to my attention.