Transform generic Measures into SVG Speedometer Graph
The stylesheet buildSVGSpeedometer.xsl will take a source document in the proper format – with a graphData root element, sets- and set-child elements and finally measure-elements that contain the actual graphdata – and transform it to a SVG object: a proper Digital Gauge chart. Here I will show a few sections from this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://xml.apache.org/xslt/java/java.lang.Math" xmlns:xlink="http://www.w3.org/2000/xlink/namespace/" > <!-- for oracle XDK: xmlns:math="http://www.oracle.com/XSL/Transform/java/java.lang.Math" for Xalan: xmlns:math="http://xml.apache.org/xslt/java/java.lang.Math" --> <!-- center of the dial: cx="2160" cy="2720" --> <xsl:output omit-xml-declaration="yes"/> <xsl:template match="graphData"> <xsl:variable name="degrees"> <!-- how far should the gauge extend; default is 180 degrees, a semi-circle --> <xsl:choose> <xsl:when test="degrees"> <xsl:value-of select="degrees"/> </xsl:when> <xsl:otherwise>180</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="max"> <xsl:value-of select="maxy"/> </xsl:variable> <xsl:variable name="min"> <xsl:value-of select="miny"/> </xsl:variable> <xsl:variable name="maxx"> <xsl:value-of select="maxx"/> </xsl:variable> <xsl:variable name="minx"> <xsl:value-of select="minx"/> </xsl:variable> <svg width="1100px" height="1850px" onload="getSVGDoc(evt)" onzoom="ZoomControl()"> <defs> <script type="text/javascript"> <![CDATA[ /* this code was largely reused from the excellent website SVG - Learning by Coding (http://svglbc.datenverdrahten.de/) */ var svgdoc,svgroot; function getSVGDoc(load_evt) { svgdoc=load_evt.target.ownerDocument; svgroot=svgdoc.documentElement; texte=svgdoc.getElementById("tooltip").getElementsByTagName("text"); } function ShowTooltip(mousemove_event,txt) { var ttrelem,tttelem,posx,posy,curtrans,ctx,cty,txt; var sollbreite,maxbreite,ges,anz,tmp,txl,neu,i,k,l ttrelem=svgdoc.getElementById("ttr"); tttelem=svgdoc.getElementById("ttt"); posx=mousemove_event.clientX; posy=mousemove_event.clientY; for(i=1;i<=5;i++)texte.item(i).firstChild.data=""; sollbreite=150; tttelem.childNodes.item(0).data=txt; ges=tttelem.getComputedTextLength(); tttelem.childNodes.item(0).data=""; anz=Math.ceil(ges/sollbreite); tmp=txt.split(" "); txl=new Array(tmp.length); neu=new Array(anz); for(i=0;i<tmp.length;i++) { tttelem.childNodes.item(0).data=tmp[i]; txl[i]=tttelem.getComputedTextLength(); } k=0; maxbreite=0; for(i=0;i<anz;i++) { l=0,neu[i]=""; while(l+txl[k]<1.1*sollbreite && k<tmp.length) { l+=txl[k]; neu[i]+=tmp[k]+" "; k++; if(maxbreite<l)maxbreite=l; } } curtrans=svgroot.currentTranslate; ctx=curtrans.x; cty=curtrans.y; ttrelem.setAttribute("x",posx-ctx+10); ttrelem.setAttribute("y",posy-cty-20+10); ttrelem.setAttribute("width",maxbreite+2*(maxbreite-sollbreite)+110); ttrelem.setAttribute("height",anz*15+3); ttrelem.setAttribute("style","fill: #FFC; stroke: #000; stroke-width: 0.5px"); for(i=1;i<=anz;i++) { texte.item(i).firstChild.data=neu[i-1]; texte.item(i).setAttribute("x",posx-ctx+15); texte.item(i).setAttribute("y",parseInt(i-1)*15+posy-cty+3); texte.item(i).setAttribute("style","fill: #00C; font-size: 11px"); } svgdoc.getElementById("tooltip").style.setProperty("visibility","visible"); } function HideTooltip() { svgdoc.getElementById("tooltip").style.setProperty("visibility","hidden"); } function ZoomControl() { var curzoom; curzoom=svgroot.currentScale; svgdoc.getElementById("tooltip").setAttribute("transform","scale("+1/curzoom+")"); } ]]> </script> <radialGradient id="GR63" gradientUnits="userSpaceOnUse" cx="2160" cy="2720" r="1940" > <stop offset="0.1" stop-color="#E7EAE9"/> <stop offset="0.95" stop-color="#9DACA8"/> </radialGradient> </defs> <g id='all' transform="scale(0.2)"> <!-- create the background in gray --> <g stroke-width="12" stroke="#000000" fill="#556475"> <rect x="40" y="40" width="4240" height="4160" rx="60" /> </g> <g id="Heading" fill="#FFFF82" font-family="Arial" font-weight="bold" font-size="180" text-anchor="middle" pointer-events="none"> <text x="2160" y="256"><xsl:value-of select="title"/></text> </g> <g id="Subhead" fill="#FFFF82" font-family="Times New Roman" font-size="100" text-anchor="middle" pointer-events="none"> <text x="2160" y="451"> <xsl:value-of select="subtitle"/> </text> </g> <!-- generate the markerareas or zones around the speedometer; each zone spans a certain value-range has a color and a tooltip --> <xsl:for-each select="markerarea"> <xsl:call-template name="markerArea"> <xsl:with-param name="startvalue" select="startvalue"/> <xsl:with-param name="endvalue" select="endvalue"/> <xsl:with-param name="min" select="$min"/> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="degrees" select="$degrees" /> <xsl:with-param name="color" select="color"/> <xsl:with-param name="title" select="title" /> </xsl:call-template> </xsl:for-each> <defs> <!-- define the gray gradient on the inside of the speedometer --> <radialGradient id="GR64" gradientUnits="userSpaceOnUse" cx="2160" cy="2720" r="1649" > <stop offset="0.1" stop-color="#E7EAE9"/> <stop offset="0.95" stop-color="#9DACA8"/> </radialGradient> </defs> <!-- inner axis area (gray semicircle)--> <g stroke="#FFFF82" stroke-width="0" stroke-linejoin="round" fill="url(#GR64)"> <xsl:variable name="markerAngle" select="math:toRadians((180 - $degrees) div 2 + $degrees)"/> <xsl:variable name="x1" select="math:cos($markerAngle) * -1* 1649 + 2160 "/> <xsl:variable name="y1" select="math:sin($markerAngle) * 1649 * -1 + 2720"/> <xsl:variable name="x2" select="math:cos($markerAngle) * 1* 1649 +2160 "/> <path> <xsl:attribute name="d"><xsl:text>M </xsl:text><xsl:value-of select="$x1"/><xsl:text> </xsl:text> <xsl:value-of select="$y1"/> <xsl:text> A 1649 1649 0 </xsl:text> <xsl:choose><xsl:when test="$degrees > 180">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose> <xsl:text> 0 </xsl:text> <xsl:value-of select="$x2"/><xsl:text> </xsl:text><xsl:value-of select="$y1"/> <xsl:text> L 2160 2720 </xsl:text> </xsl:attribute> </path> </g> <!-- inner area --> <!-- draw markers and values around the outline of the speedometer --> <xsl:for-each select="yvalues"> <xsl:for-each select="yvalue"> <xsl:call-template name="axisMarker"> <xsl:with-param name="markerValue" select="value"/> <xsl:with-param name="markerLabel" select="label"/> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="min" select="$min"/> <xsl:with-param name="degrees" select="$degrees"/> </xsl:call-template> </xsl:for-each> <xsl:for-each select="ymarkers"> <!-- draw y-markers on the axis --> <xsl:call-template name="ymarker"> <xsl:with-param name="start" select="minvalue"/> <xsl:with-param name="n" select="steps"/> <xsl:with-param name="stepsize" select="(maxvalue - minvalue) div steps"/> <xsl:with-param name="min" select="$min"/> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="gridline" select="gridline"/> <xsl:with-param name="degrees" select="$degrees"/> <xsl:with-param name="submarkers" select="submarkers"/> <xsl:with-param name="showsubmarkervalue" select="showsubmarkervalue"/> </xsl:call-template> </xsl:for-each> </xsl:for-each> <!-- yvalues --> <!-- display the counter in the middle of the speedometer --> <xsl:if test="counter"> <g id="counter" onmouseout="HideTooltip(evt)"> <xsl:attribute name="onmouseover">ShowTooltip(evt,'<xsl:value-of select="counterlabel"/>')</xsl:attribute> <rect x="1910" y="2100" width="500" height="120" fill="black" /> <text stroke="ivory" fill="ivory" font="Helvetica" text-anchor="middle" font-size="100" x="2160" y="2200"> <xsl:value-of select="counter"/> </text> <text stroke="steelblue" fill="steelblue" font="Helvetica" text-anchor="middle" font-size="70" x="2160" y="2295"> <xsl:value-of select="counterunits"/> </text> </g> </xsl:if> <!-- display the daycounter in the middle of the speedometer --> <xsl:if test="daycounter"> <g id="daycounter" onmouseout="HideTooltip(evt)"> <xsl:attribute name="onmouseover">ShowTooltip(evt,'<xsl:value-of select="daycounterlabel"/>')</xsl:attribute> <rect x="1970" y="1500" width="380" height="120" fill="black" /> <text stroke="ivory" fill="ivory" font="Helvetica" text-anchor="middle" font-size="100" x="2160" y="1600"> <xsl:value-of select="daycounter"/> </text> <text stroke="steelblue" fill="steelblue" font="Helvetica" text-anchor="middle" font-size="70" x="2160" y="1690"> <xsl:value-of select="daycounterunits"/> </text> </g> </xsl:if> <!-- Draw a dial for each set in the source document --> <xsl:for-each select="sets/set"> <xsl:variable name="color"> <xsl:choose> <xsl:when test="@color"><xsl:value-of select="@color"/></xsl:when> <!-- if no color was defined, assign a color --> <xsl:when test="position()=1">#000080</xsl:when> <xsl:when test="position()=2">#800000</xsl:when> <xsl:when test="position()=3">green</xsl:when> <xsl:when test="position()=3">ivory</xsl:when> </xsl:choose> </xsl:variable> <g onmouseout="HideTooltip(evt)"> <xsl:attribute name="onmouseover">ShowTooltip(evt,'<xsl:value-of select="measure[1]/label"/>')</xsl:attribute> <xsl:attribute name="transform">rotate(<xsl:value-of select="(180 - $degrees) div 2 + (measure[1]/xvalue - $min) div ($max - $min) * $degrees"/> ,2160, 2720)</xsl:attribute> <line fill="none" stroke-width="44" x1="2160" y1="2720" y2="2720"> <xsl:attribute name="stroke"><xsl:value-of select="$color"/></xsl:attribute> <xsl:attribute name="x2"><xsl:value-of select="297 + (position() -1 ) * 550"/></xsl:attribute> </line> <g stroke-width="42" > <xsl:attribute name="stroke"><xsl:value-of select="$color"/></xsl:attribute> <xsl:attribute name="fill"><xsl:value-of select="$color"/></xsl:attribute> <path > <xsl:attribute name="d"> <xsl:text>M </xsl:text> <xsl:value-of select="297 + (position() -1 ) * 550"/> <xsl:text> 2720 L </xsl:text> <xsl:value-of select="414 + (position() -1 ) * 550"/> <xsl:text> 2804 </xsl:text> <xsl:value-of select="363 + (position() -1 ) * 550"/> <xsl:text> 2720 </xsl:text> <xsl:value-of select="414 + (position() -1 ) * 550"/> <xsl:text> 2636 z </xsl:text> </xsl:attribute> </path> </g> </g> </xsl:for-each><!-- end dials --> <!-- the outline for the gauge --> <g stroke="#000000" stroke-width="30" stroke-linejoin="round" fill="none"> <xsl:variable name="markerAngle" select="math:toRadians((180 - $degrees) div 2 + $degrees)"/> <xsl:variable name="x1" select="math:cos($markerAngle) * -1* 1940 + 2160 "/> <xsl:variable name="y1" select="math:sin($markerAngle) * 1940 * -1 + 2720"/> <xsl:variable name="x2" select="math:cos($markerAngle) * 1* 1940 +2160 "/> <path> <xsl:attribute name="d"><xsl:text>M </xsl:text><xsl:value-of select="$x1"/><xsl:text> </xsl:text> <xsl:value-of select="$y1"/> <xsl:text> A 1940 1940 0 </xsl:text> <xsl:choose><xsl:when test="$degrees>180">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose> <xsl:text> 0 </xsl:text> <xsl:value-of select="$x2"/><xsl:text> </xsl:text><xsl:value-of select="$y1"/></xsl:attribute> </path> </g> <!-- the inner circle ; the centre where the dials are 'connected'--> <g fill="#000000" stroke="#000000" stroke-width="12" > <circle cx="2160" cy="2720" r="60"/> </g> </g> <g id="tooltip" style="visibility: hidden"> <!-- Tooltip - Beginn (ttr=Tooltip-Rechteck, ttt=Tooltip-Text) --> <rect id="ttr" x="0" y="0" rx="5" ry="5" width="150" height="16"/> <text id="ttt" x="0" y="0" style="visibility: hidden">dyn. Text</text> <text x="-10" y="-10">dyn. Text</text> <text x="-10" y="-10">dyn. Text</text> <text x="-10" y="-10">dyn. Text</text> <text x="-10" y="-10">dyn. Text</text> <text x="-10" y="-10">dyn. Text</text> </g> <!-- Tooltip - End --> </svg> </xsl:template> <!-- draw markers along the outside of the speedometer; also display labels (usually values) --> <xsl:template name="axisMarker"> <xsl:param name="markerValue"/> <xsl:param name="markerLabel"><xsl:value-of select="$markerValue"/></xsl:param> <xsl:param name="max"/> <xsl:param name="min"/> <xsl:param name="degrees">180</xsl:param> <xsl:param name="sub">0</xsl:param> <xsl:variable name="markerAngle" select="math:toRadians((180 - $degrees) div 2 + ($markerValue - $min) div ($max - $min) * $degrees)"/> <!-- if submarker then value should be more pushed out --> <xsl:variable name="x1"> <xsl:choose> <xsl:when test="$sub = 1"> <xsl:value-of select="math:cos($markerAngle) * -1* 1640 + 2160 "/> </xsl:when> <xsl:otherwise> <xsl:value-of select="math:cos($markerAngle) * -1* 1580 + 2160 "/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:variable name="y1"> <xsl:choose> <xsl:when test="$sub = 1"> <xsl:value-of select="math:sin($markerAngle) * 1500 * -1 + 2735 "/> </xsl:when> <xsl:otherwise> <xsl:value-of select="math:sin($markerAngle) * 1480 * -1 + 2755 "/> </xsl:otherwise> </xsl:choose> </xsl:variable> <text font-family="Arial" font-weight="bold" > <xsl:attribute name="font-size"> <xsl:choose> <xsl:when test="$sub = 1">100</xsl:when> <xsl:otherwise>160</xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:attribute name="text-anchor"> <xsl:choose> <xsl:when test="$markerAngle < 1 ">start</xsl:when> <xsl:when test="($markerAngle > 2) ">end</xsl:when> <xsl:otherwise>middle</xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:attribute name="x"><xsl:value-of select="$x1"/></xsl:attribute> <xsl:attribute name="y"><xsl:value-of select="$y1"/></xsl:attribute> <xsl:value-of select="$markerLabel"/> </text> <g fill="blue" stroke="#000000" > <xsl:attribute name="stroke-width"> <xsl:choose> <xsl:when test="$sub = 1">15</xsl:when> <xsl:otherwise>22</xsl:otherwise> </xsl:choose> </xsl:attribute> <xsl:attribute name="transform">rotate(<xsl:value-of select="(180- $degrees) div 2 + ($markerValue - $min) div ($max - $min) * $degrees"/>, 2160, 2720)</xsl:attribute> <line x1="220" y1="2720" y2="2720"> <xsl:attribute name="x2"> <xsl:choose> <xsl:when test="$sub = 1">350</xsl:when> <xsl:otherwise>558</xsl:otherwise> </xsl:choose> </xsl:attribute> </line> </g> </xsl:template><!-- axisMarker --> <xsl:template name="ymarker"> <xsl:param name="n"/> <xsl:param name="i">0</xsl:param> <xsl:param name="stepsize"/> <xsl:param name="min"/> <xsl:param name="max"/> <xsl:param name="gridline">false</xsl:param> <xsl:param name="start"> <xsl:value-of select="$min"/> </xsl:param> <xsl:param name="axis">1</xsl:param> <xsl:param name="degrees">180</xsl:param> <xsl:param name="submarkers">0</xsl:param> <xsl:param name="showsubmarkervalue">no</xsl:param> <xsl:variable name="value"> <xsl:value-of select="(($start + $i* $stepsize ) )"/> </xsl:variable> <xsl:call-template name="axisMarker"> <xsl:with-param name="markerValue" select="$value"/> <xsl:with-param name="markerLabel" select="$value"/> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="min" select="$min"/> <xsl:with-param name="degrees" select="$degrees"/> </xsl:call-template> <xsl:if test="$i < $n"> <xsl:call-template name="ymarker"> <xsl:with-param name="i" select="$i+1"/> <xsl:with-param name="n" select="$n"/> <xsl:with-param name="stepsize" select="$stepsize"/> <xsl:with-param name="min" select="$min"/> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="start" select="$start"/> <xsl:with-param name="gridline" select="$gridline"/> <xsl:with-param name="axis" select="$axis"/> <xsl:with-param name="submarkers" select="$submarkers"/> <xsl:with-param name="showsubmarkervalue" select="$showsubmarkervalue"/> <xsl:with-param name="degrees" select="$degrees"/> </xsl:call-template> <!-- if so desired and still within the range set up by the y-markers then draw submarkers --> <xsl:if test="$submarkers > 0"> <xsl:call-template name="submarkers"> <xsl:with-param name="i" select="1"/> <xsl:with-param name="n" select="$submarkers"/> <xsl:with-param name="stepsize" select="$stepsize div ($submarkers +1)"/> <xsl:with-param name="min" select="$min"/> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="start" select="$value"/> <xsl:with-param name="gridline" select="$gridline"/> <xsl:with-param name="axis" select="$axis"/> <xsl:with-param name="showsubmarkervalue" select="$showsubmarkervalue"/> <xsl:with-param name="degrees" select="$degrees"/> </xsl:call-template> </xsl:if> </xsl:if> </xsl:template> <!-- ymarker --> <!-- show the smaller markers (submarkers) --> <xsl:template name="submarkers"> <xsl:param name="n"/> <xsl:param name="i">0</xsl:param> <xsl:param name="stepsize"/> <xsl:param name="min"/> <xsl:param name="max"/> <xsl:param name="gridline">false</xsl:param> <xsl:param name="start"> <xsl:value-of select="$min"/> </xsl:param> <xsl:param name="axis">1</xsl:param> <xsl:param name="degrees">180</xsl:param> <xsl:param name="submarkers">0</xsl:param> <xsl:param name="showsubmarkervalue">no</xsl:param> <xsl:variable name="value"> <xsl:value-of select="(($start + $stepsize ) )"/> </xsl:variable> <xsl:call-template name="axisMarker"> <xsl:with-param name="markerValue" select="$value"/> <xsl:with-param name="markerLabel"> <xsl:choose> <xsl:when test="showsubmarkervalue='yes'"><xsl:value-of select="$value" /> </xsl:when> </xsl:choose> </xsl:with-param> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="min" select="$min"/> <xsl:with-param name="degrees" select="$degrees"/> <xsl:with-param name="sub">1</xsl:with-param> </xsl:call-template> <!-- if submarkers > 0 then substepsize = divide stepsize by submarkers; --> <xsl:if test="$i < $n"> <xsl:call-template name="submarkers"> <xsl:with-param name="i" select="$i + 1"/> <xsl:with-param name="n" select="$n"/> <xsl:with-param name="stepsize" select="$stepsize"/> <xsl:with-param name="min" select="$min"/> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="start" select="$value"/> <xsl:with-param name="gridline" select="$gridline"/> <xsl:with-param name="axis" select="$axis"/> <xsl:with-param name="submarkers" select="$submarkers"/> <xsl:with-param name="showsubmarkervalue" select="$showsubmarkervalue"/> <xsl:with-param name="degrees" select="$degrees"/> </xsl:call-template> </xsl:if> </xsl:template> <!-- submarkers --> <!-- draw the markerareas - the zones on the outside of the speedometer --> <xsl:template name="markerArea"> <xsl:param name="startvalue" /> <xsl:param name="endvalue" /> <xsl:param name="min" /> <xsl:param name="max" /> <xsl:param name="degrees">180</xsl:param> <xsl:param name="color">blue</xsl:param> <xsl:param name="title">myArea</xsl:param> <!-- startvalue to endvalue --> <g stroke-width="0" stroke-linejoin="round" onmouseout="HideTooltip(evt)"> <xsl:attribute name="onmouseover">ShowTooltip(evt,'<xsl:value-of select="$title"/>')</xsl:attribute> <xsl:attribute name="stroke"><xsl:value-of select="$color"/></xsl:attribute> <xsl:attribute name="fill"><xsl:value-of select="$color"/></xsl:attribute> <xsl:variable name="startvalue" select="$startvalue"/> <xsl:variable name="endvalue" select="$endvalue"/> <xsl:variable name="firstAngle" select="math:toRadians((180 - $degrees) div 2 + ($endvalue - $min) div ($max - $min) * $degrees)"/> <xsl:variable name="secondAngle" select="math:toRadians((180 - $degrees) div 2 + ($startvalue - $min) div ($max - $min) * $degrees)"/> <xsl:variable name="x1" select="math:cos($firstAngle) * -1* 1940 +2160 "/> <xsl:variable name="y1" select="math:sin($firstAngle) * 1940 * -1 + 2720"/> <xsl:variable name="x2" select="math:cos($secondAngle) * -1* 1940 +2160 "/> <xsl:variable name="y2" select="math:sin($secondAngle) * 1940 * -1 + 2720"/> <path > <xsl:attribute name="angle"><xsl:value-of select="$firstAngle"/></xsl:attribute> <xsl:attribute name="d"><xsl:text>M 2160 2720 L </xsl:text><xsl:value-of select="$x1"/><xsl:text> </xsl:text> <xsl:value-of select="$y1"/> <xsl:text> A 1940 1940 0 </xsl:text> <xsl:choose><xsl:when test="($secondAngle - $firstAngle) > 180">1</xsl:when><xsl:otherwise>0</xsl:otherwise></xsl:choose> <xsl:text> 0 </xsl:text> <xsl:value-of select="$x2"/><xsl:text> </xsl:text><xsl:value-of select="$y2"/> <xsl:text> z </xsl:text> </xsl:attribute> </path> </g> </xsl:template> </xsl:stylesheet>
The result of the transformation with this stylesheet to the SVG object can be downloaded here:DepSalSpeedometer.svg. The graph that is displayed as a result from these transformations:
I can understand the report in PDF,XML,HTML.
I want to do the report in XL.How I can proceed please give a idea.
all information resides here are good.i need some information regardingg migration.i.e, can i get sorce code & front end design for migrating data from excel to oracle.reply soon…plz…
I have drawn up a list of to do’s: new features I would like to add to the Speedometer chart:
– animation (show the movement of the dials over time)
– support for multiple axes
– better markersupport: set markercolor, markerdash and markerlength; gridlines
– display images (icons) as (part of) markervalues
– display a legend indicating the meaning of each dial
– implement a clock using the speedometer stylesheet as demonstration
I hope to add these features in the new couple of weeks and update the post accordingly.