Search code examples
xpathsvgxqueryexist-db

How do I create bar-charts in SVG from my XML using XQuery and/or XSLT in eXist-db


I can't find any existing solutions because I do not think that anybody else has used eXist-db to do this yet. Does anybody know how to draw bar charts using SVG from eXist-db?

Firstly date:

DTD:

<?xml version="1.0" encoding="utf-8"?>
<!ELEMENT akweny (akwen+)>
<!ELEMENT akwen (nazwa, typ, powierzchnia, akweny?)>
<!ELEMENT nazwa (#PCDATA)>
<!ELEMENT typ (#PCDATA)>
<!ELEMENT powierzchnia (#PCDATA)>

And XML for example:

<?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE akweny SYSTEM "akweny.dtd">
    <akweny>
        <akwen>
            <nazwa>Atlantycki</nazwa>
            <typ>ocean</typ>
            <powierzchnia>106450</powierzchnia>
            <akweny>
                <akwen>
                    <nazwa>Północne</nazwa>
                    <typ>morze</typ>
                    <powierzchnia>750</powierzchnia>
                </akwen>
                <akwen>
                    <nazwa>Batyckie</nazwa>
                    <typ>morze</typ>
                    <powierzchnia>386</powierzchnia>
                    <akweny>
                        <akwen>
                            <nazwa>Botnicka</nazwa>
                            <typ>zatoka</typ>
                            <powierzchnia>117</powierzchnia>
                        </akwen>
                    </akweny>
                </akwen>
            </akweny>
        </akwen>
        <akwen>
            <nazwa>Spokojny</nazwa>
            <typ>ocean</typ>
            <powierzchnia>179700</powierzchnia>
        </akwen>
    </akweny>

I know eXist-db has some XSLT transformation functions, e.g.

transform:stream-transform($node-tree as node()*, $stylesheet as item(), 
$parameters as node()?) as item()

But how the hell can I use this? I can't find any examples. I don know how to use it...

Here is my link:

bars.xq?nazwa=anyNazwa&typ=anyTyp&powierzchnia=100

It should show Any of nazwa, any of typ and greater than powierzchnia of 100.

Here is my simple XQuery

let $nodes := doc('/db/Dane/akweny.xml')//akweny[ancestor::akwen/nazwa=request:get-parameter("nazwa", ()) and typ=request:get-parameter("typ", ())]
        for $x in $nodes/* 
        let $nazwa := $x/nazwa/text()
        let $typ := $x/typ/text()
        let $powierzchnia := $x/powierzchnia/text()
            where $powierzchnia >= number(request:get-parameter("powierchnia", ()))

My question is:

How to show all the children of the selected node by: nazwa, typ and powierchnia in the form of a bar chart?


Solution

  • Here is an example of doing this in pure XQuery, this will work in eXist, or any other XQuery 1.0 processor:

    declare variable $local:max-graph-height := 400; (: pixels :)
    declare variable $local:graph-bar-width := 120; (: pixels :)
    declare variable $local:graph-bar-spacing := 10; (: pixels :)
    declare variable $local:graph-text-below := 20; (: pixels :)
    
    let $awkeny := doc("/db/Dane/akweny.xml")//akwen[xs:int(powierzchnia) gt 100]
    let $max-powierzchnia := max($awkeny/powierzchnia)
    
    return
    
        <svg xmlns="http://www.w3.org/2000/svg" 
            xmlns:xlink="http://www.w3.org/1999/xlink" 
            id="graph"
            height="{ $local:max-graph-height + ($local:graph-text-below * 2) }" 
            width="{ ($local:graph-bar-width + $local:graph-bar-spacing) * count($awkeny) }">
    
            <linearGradient x1="0%" x2="0%" y1="10%" y2="100%" id="gradient">
                <stop style="stop-color:#0000FF" offset="0"></stop>
                <stop style="stop-color:#FFFFFF" offset="1"></stop>
            </linearGradient>
    
            {
            for $awken at $i in $awkeny
            let $height := (xs:float($awken/*:powierzchnia) div $max-powierzchnia) * $local:max-graph-height
            let $x := ($i - 1) * ($local:graph-bar-with + $local:graph-bar-spacing)
            let $y := $local:max-graph-height - $height
            return
                (
                <rect width="{$local:graph-bar-with}" 
                    height="{$height}" x="{$x}" y="{$y}" 
                    fill="url(#gradient)"></rect>,
                <text x="{$x}" 
                    y="{$local:max-graph-height + $local:graph-text-below}" 
                    fill="red">{$awken/*:nazwa/text()}</text>
                )
            }
        </svg>
    

    The above XQuery when run on your awkeny.xml produces a bar-chart in SVG that looks like the following: SVG Bar Chart of awkeny.xml generated from XQuery

    Now I think you just need to work that into your XQuery in eXist as you appear from your example to be taking parameters from the incoming HTTP Request, however I can see from your question that you already know how to do that. So you should now be well on your way...