W3C home > Mailing lists > Public > www-svg@w3.org > July 2001

Problem with the position() function and SVG

From: Panie, Edouard (MED) <Edouard.Panie@med.ge.com>
Date: Mon, 16 Jul 2001 17:25:57 +0200
Message-ID: <ED15557B95C6D411BC5B00105A5A233C383B16@frbucmsx10medge>
To: "'www-svg@w3.org'" <www-svg@w3.org>
Hello,
I'm learning SVG and I found DougTidwell "XML to SVG tutorial" on the
developersworks page.
http://www-106.ibm.com/developerworks/education/transforming-xml/xmltosvg/in
dex.html#3

It's a great tutorial! Unfortunately I have a little problem when I try the
sales histogram example. I copied the sales.xml, sales.dtd and sales.xsl
files. It almost works but not perfectly: it works a little bit because I do
get a graph with the axes and columns with the good height but all the
columns are on top one another, it seems to me that the position() function
doesn't work. Do I need to install something special in order for the
position() function to work or to include a package in the header of the xsl
doc??? 

What I did until now: I downloaded the xalan-j_2_2_D6, I set the classpath
as showed below, then in a DOS command window, I go to the directory where I
stored the .xml,  .dtd and .xsl files and call: D:\mySamplesFolder>java
org.apache.xalan.xslt.Process -in sales.xml -xsl sales.xsl -out
theResult.svg

CLASSPATH=c:\Program
Files\Exceed.nt;d:\tools\xalan-j_2_2_D6\bin\xerces.jar;d:\tools\xalan-j_2_2_
D6\bin\xalan.jar;c:\jdk1.1.8\lib\classes.zip;d:\tools\xalan-j_2_2_D6\bin\xal
ansamples.jar

Thanks a lot for your help
Edouard Paniť

Here is the xsl file I use and the svg output:


////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/
sales.xsl
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"
doctype-system="svg-19991203.dtd"
version="1.0"
encoding="ISO-8859-1" />
<xsl:strip-space elements="*"/>

<!--================================================================-->
<!-- Root Element Template                                          -->
<!-- This template specifies what creates the root element of the   -->
<!-- result tree.  In this case, it tells the XSL processor to      -->
<!-- start with the <sales> element.                                -->
<!--================================================================-->

<xsl:template match="/">
<xsl:apply-templates select="sales"/>
</xsl:template>

<!--================================================================-->
<!-- <sales> template                                               -->
<!--                                                                -->
<!-- This template creates the entire SVG bar chart.  The title of  -->
<!-- the chart comes from the <heading> element, and the size and   -->
<!-- position of the bars in the graph are calculated by adding the -->
<!-- <product> elements inside each <region> element.  In addition, -->
<!-- the labels in the graph legend are generated from the text of  -->
<!-- the <name> element inside each <region> element.               -->
<!--                                                                -->
<!-- To simplify the stylesheet, we hardcoded the scale, the dashed -->
<!-- lines behind the graph, and other assumptions about how much   -->
<!-- space we'll need, etc.  You could certainly add more variables -->
<!-- and parameters to the templates to make the stylesheet more    -->
<!-- robust....                                                     -->
<!--================================================================-->

<xsl:template match="sales">
<svg width="400" height="300">

<!--================================================================-->
<!-- Create the title for the bar chart.  We'll use the text of the -->
<!-- <heading> element for this.  Before we output the text, we'll  -->
<!-- set up the SVG <text> element with the appropriate style, x,   -->
<!-- and y attributes.  We hardcoded the x and y values to simplify -->
<!-- things; it would complicate the stylesheet significantly to    -->
<!-- figure out how wide the entire chart needed to be, then anchor -->
<!-- the title in the middle.                                       -->
<!--================================================================-->

<text style="font-size:18; text-anchor:middle" x="120" y="20">
<xsl:value-of select="caption/heading"/>
</text>

<!--================================================================-->
<!-- Now we print the subtitle of the bar chart.  This text doesn't -->
<!-- change, so we just hardcode the SVG element here.              -->
<!--================================================================-->

<text style="font-size:12; text-anchor:middle" 
y="33" x="120">(in millions of dollars)</text>

<!--================================================================-->
<!-- We hardcode the X and Y axis and the grid lines.  Note that we -->
<!-- use a gray color and the stroke-dasharray properties to make   -->
<!-- the lines lighter.  Also, because all SVG drawing operations   -->
<!-- are opaque by default, we draw the grid lines first.  That     -->
<!-- means everything we draw later will appear on top of the grid  -->
<!-- lines.                                                         -->
<!--                                                                -->
<!-- A note about the d attribute of the <path> elements:           -->
<!-- The values here contain move (M) commands, lineto (L) commands,-->
<!-- and closepath (Z) commands.  The first <path> element here     -->
<!-- tells the SVG rendering engine to move to x,y coordinate       -->
<!-- (40, 200), draw a line to (40, 30), then close the path. We'll -->
<!-- use this syntax throughout the stylesheet.  See the SVG spec   -->
<!-- for more information about paths and other drawing commands.   -->
<!--================================================================-->

<g style="stroke-width:2; stroke:black">
<path d="M 40 220 L  40  30 L 40 220 L 200 220 Z"/>
</g>
<g style="fill:none; stroke:#B0B0B0; stroke-width:1; 
  stroke-dasharray:2 4">
<path d="M 42 200 L 198 200 Z"/>
<path d="M 42 180 L 198 180 Z"/>
<path d="M 42 160 L 198 160 Z"/>
<path d="M 42 140 L 198 140 Z"/>
<path d="M 42 120 L 198 120 Z"/>
<path d="M 42 100 L 198 100 Z"/>
<path d="M 42  80 L 198  80 Z"/>
<path d="M 42  60 L 198  60 Z"/>
<path d="M 42  40 L 198  40 Z"/>
</g>

<!--================================================================-->
<!-- We hardcoded the labels here as well.  Each one is written to  -->
<!-- the left of the grid lines, and is right-justified (that's     -->
<!-- what text-anchor:end does).                                    -->
<!--================================================================-->

<g style="text-anchor:end; font-size:9">
<text x="36" y="203">20</text>
<text x="36" y="183">40</text>
<text x="36" y="163">60</text>
<text x="36" y="143">80</text>
<text x="36" y="123">100</text>
<text x="36" y="103">120</text>
<text x="36" y="83">140</text>
<text x="36" y="63">160</text>
<text x="36" y="43">$180</text>
</g>

<!--================================================================-->
<!-- Now it's time to get to work.  For each region we're going to  -->
<!-- represent in the bar chart, we'll need several things:         -->
<!--                                                                -->
<!--   1) The x-coordinate where the bar should start               -->
<!--   2) The y-coordinate where the bar should start               -->
<!--   3) The color we should use to fill in the bar                -->
<!--   4) The y-coordinate where the legend entry should start.     -->
<!--                                                                -->
<!-- We'll use the variables $x-offset, $y-offset, $color, and      -->
<!-- $y-legend-offset to represent these things.  We'll update      -->
<!-- these variables during each iteration of the <xsl:for-each>    -->
<!-- element.                                                       -->
<!--================================================================-->

<!--================================================================-->
<!-- We use the <for-each> element to loop through all of the       -->
<!-- instances of the <region> tag.                                 -->
<!--================================================================-->

<xsl:for-each select="region">

<!--================================================================-->
<!-- Now that we've set up all the variables we'll need, we'll      -->
<!-- invoke the template for the <region> element.                  -->
<!--================================================================-->

<xsl:apply-templates select=".">

<!--================================================================-->
<!-- We use the position of this region element to determine the    -->
<!-- color.  The combination of the position() function from XPath  -->
<!-- and the mod operator lets us cycle through five different      -->
<!-- colors.                                                        -->
<!--================================================================-->

<xsl:with-param name="color">
<xsl:choose>
<xsl:when test="(position() mod 5) = 1">
  <xsl:text>red</xsl:text>
</xsl:when>
<xsl:when test="(position() mod 5) = 2">
  <xsl:text>yellow</xsl:text>
</xsl:when>
<xsl:when test="(position() mod 5) = 3">
  <xsl:text>purple</xsl:text>
</xsl:when>
<xsl:when test="(position() mod 5) = 4">
  <xsl:text>blue</xsl:text>
</xsl:when>
<xsl:otherwise>
  <xsl:text>green</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>

<xsl:with-param name="y-legend-offset">
<xsl:value-of select="60 + (position() * 30)"/>
</xsl:with-param>

<!--================================================================-->
<!-- For the $x-offset, we use the position of the current region.  -->
<!--================================================================-->

<xsl:with-param name="x-offset">
<xsl:value-of select="30 + (position() * 30)"/>
</xsl:with-param>

<!--================================================================-->
<!-- Notice that we just hardcoded $y-offset....                    -->
<!--================================================================-->

<xsl:with-param name="y-offset" select="220"/>
</xsl:apply-templates>

</xsl:for-each>
</svg>
</xsl:template>

<!--================================================================-->
<!-- <region> template                                              -->
<!--                                                                -->
<!-- This template generates the bar chart data for each region.    -->
<!-- For each region, we have to output four things:                -->
<!--                                                                -->
<!--   1) The bar itself.  The bar's height is determined by the    -->
<!--      sales data from the XML document, and it's fill color is  -->
<!--      passed in on the $color parameter.                        -->
<!--   2) The sales total.  This is drawn as text, centered just    -->
<!--      above the bar.  The value is calculated from the XML.     -->
<!--   3) The text of the legend.  This is the <name> element.  It  -->
<!--      is drawn based on the $y-legend-offset.                   -->
<!--   4) The color bar for the legend.  This is a block filled in  -->
<!--      based on the $color parameter.  Its position is based on  -->
<!--      the $y-legend-offset.                                     -->
<!--================================================================-->

<xsl:template match="region">

<!--================================================================-->
<!-- Our four parameters are the x- and y-offsets where the bar     -->
<!-- should start, the color used to fill in the bar, and the       -->
<!-- y-offset where the legend entry for this <region> should start.-->
<!--================================================================-->

<xsl:param name="x-offset" select="'60'"/>
<xsl:param name="y-offset" select="'220'"/>
<xsl:param name="color" select="'red'"/>
<xsl:param name="y-legend-offset" select="'90'"/>

<!--================================================================-->
<!-- We'll calculate the y value for the top of the bar based on    -->
<!-- the total sales for this <region>.  This value is subtracted   -->
<!-- from the y-offset from which we started.                       -->
<!--================================================================-->

<xsl:variable name="y">
<xsl:value-of select="$y-offset - sum(product)"/>
</xsl:variable>

<!--================================================================-->
<!-- Task 1:  Draw the bar                                          -->
<!--                                                                -->
<!-- For this task, we'll create an SVG <path> element.  We'll set  -->
<!-- the fill property so the bar will be filled with the correct   -->
<!-- color.  Notice that fill is a property set with the XML style  -->
<!-- attribute; SVG does this to be consistent with CSS, even       -->
<!-- though it creates some confusion as to which things are        -->
<!-- attributes and which things are properties set in the text of  -->
<!-- attributes.                                                    -->
<!--                                                                -->
<!-- Once we've created the style attribute and set the fill        -->
<!-- property correctly, we'll create the d attribute.  The d       -->
<!-- attribute contains five commands:                              -->
<!--                                                                -->
<!--   1) Move to the origin of the bar.                            -->
<!--   2) Draw a line to the upper left corner of the bar.          -->
<!--   3) Draw a line to the upper right corner of the bar.         -->
<!--   4) Draw a line to the lower right corner of the bar.         -->
<!--   5) Close the path.  This draws a line from wherever we are   -->
<!--      to the start of the path.                                 -->
<!--                                                                -->
<!-- The move command is M, the lineto command is L, and the        -->
<!-- closepath command is Z.                                        -->
<!--================================================================-->

<path>
<xsl:attribute name="style">
<xsl:text>stroke-width:2; stroke:black; fill:</xsl:text>
<xsl:value-of select="$color"/>
</xsl:attribute>
<xsl:attribute name="d">

<!--================================================================-->
<!-- Move to the origin of the bar.  The $x-offset parameter is the -->
<!-- center of the bar, so it begins 10 units to the left of        -->
<!-- $x-offset ($x-offset - 10), and ends 10 units to the right     -->
<!-- ($x-offset + 10).  The $y-offset is used as is.                -->
<!--================================================================-->

<xsl:text>M </xsl:text>
<xsl:value-of select="$x-offset - 10"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$y-offset"/>

<!--================================================================-->
<!-- Move to the upper left corner of the bar.  The x dimension is  -->
<!-- ($x-offset - 10), and the y dimension is the $y variable we    -->
<!-- calculated earlier.                                            -->
<!--================================================================-->

<xsl:text> L </xsl:text>
<xsl:value-of select="$x-offset - 10"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$y"/>

<!--================================================================-->
<!-- Move to the upper right corner of the bar.  The x dimension is -->
<!-- ($x-offset + 10), and the y dimension is the $y variable we    -->
<!-- calculated earlier.                                            -->
<!--================================================================-->

<xsl:text> L </xsl:text>
<xsl:value-of select="$x-offset + 10"/>
<xsl:text> </xsl:text>
<xsl:value-of select="$y"/>

<!--================================================================-->
<!-- Move to the lower right corner of the bar.  The x dimension is -->
<!-- ($x-offset + 10), and the y dimension is the $y-offset value   -->
<!-- that was passed in.                                            -->
<!--================================================================-->

<xsl:text> L </xsl:text>
<xsl:value-of select="$x-offset + 10"/>
<xsl:text> </xsl:text> 
<xsl:value-of select="$y-offset"/>

<!--================================================================-->
<!-- Close the path.  All we need is a Z command.                   -->
<!--================================================================-->

<xsl:text> Z</xsl:text>
</xsl:attribute>
</path>

<!--================================================================-->
<!-- Task 2:  Draw the sales total.                                 -->
<!--                                                                -->
<!-- For this task, we'll create an SVG <text> element.  It will be -->
<!-- centered (text-anchor:middle, a property we inherit from an    -->
<!-- earlier <g> element) and drawn just above the top of the bar.  -->
<!-- The center of the bar is $x-offset, which we received as a     -->
<!-- parameter, and the top of the bar is $y.  We'll subtract 5     -->
<!-- units from $y to calculate its starting point.  The value we   -->
<!-- print is based on the sum of all <product> elements contained  -->
<!-- inside this <region>.                                          -->
<!--================================================================-->

<text style="font-size:10; text-anchor:middle">
<xsl:attribute name="x">
<xsl:value-of select="$x-offset"/>
</xsl:attribute>
<xsl:attribute name="y">
<xsl:value-of select="$y - 5"/>
</xsl:attribute>
<xsl:value-of select="sum(product)"/>
</text>

<!--================================================================-->
<!-- Task 3:  Draw the text of the legend.                          -->
<!--                                                                -->
<!-- For this task, we'll use the <name> element contained inside   -->
<!-- this <region>.  We create an SVG <text> element to do this.    -->
<!-- The x coordinate of the text is 240, while the y coordinate is -->
<!-- based on the $y-legend-offset.  We simply fill in the style,   -->
<!-- x, and y attributes of the <text> tag, then use the value of   -->
<!-- the <name> element as the text of the new tag.                 -->
<!--================================================================-->

<text style="font-size:14; text-anchor:start" x="240">
<xsl:attribute name="y">
<xsl:value-of select="$y-legend-offset"/>
</xsl:attribute>
<xsl:value-of select="name"/>
</text>

<!--================================================================-->
<!-- Task 4. Draw the color bar for the legend.                     -->
<!--                                                                -->
<!-- For this task, we'll draw a small box that is filled in with   -->
<!-- the correct color.  The position of the box is based on the    -->
<!-- hardcoded x coordinate of 220, and the $y-legend-offset we got -->
<!-- as a parameter.  The box is 10 x 10 units.                     -->
<!--================================================================-->

<path>
<xsl:attribute name="style">
<xsl:text>stroke:black; stroke-width:2; fill:</xsl:text> 
<xsl:value-of select="$color"/>
</xsl:attribute> 
<xsl:attribute name="d">
<xsl:text>M 220 </xsl:text> 
<xsl:value-of select="$y-legend-offset - 10"/>
<xsl:text> L 220 </xsl:text> 
<xsl:value-of select="$y-legend-offset"/>
<xsl:text> L 230 </xsl:text> 
<xsl:value-of select="$y-legend-offset"/>
<xsl:text> L 230 </xsl:text> 
<xsl:value-of select="$y-legend-offset - 10"/>
<xsl:text> Z</xsl:text> 
</xsl:attribute>
</path>
</xsl:template>

</xsl:stylesheet>


















////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/
theResult.svg
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE svg SYSTEM "svg-19991203.dtd">
<svg height="300" width="400">
<text y="20" x="120" style="font-size:18; text-anchor:middle">3Q 2000 Sales
Figures</text>
<text x="120" y="33" style="font-size:12; text-anchor:middle">(in millions
of dollars)</text>
<g style="stroke-width:2; stroke:black">
<path d="M 40 220 L  40  30 L 40 220 L 200 220 Z"/>
</g>
<g style="fill:none; stroke:#B0B0B0; stroke-width:1;
stroke-dasharray:2 4">
<path d="M 42 200 L 198 200 Z"/>
<path d="M 42 180 L 198 180 Z"/>
<path d="M 42 160 L 198 160 Z"/>
<path d="M 42 140 L 198 140 Z"/>
<path d="M 42 120 L 198 120 Z"/>
<path d="M 42 100 L 198 100 Z"/>
<path d="M 42  80 L 198  80 Z"/>
<path d="M 42  60 L 198  60 Z"/>
<path d="M 42  40 L 198  40 Z"/>
</g>
<g style="text-anchor:end; font-size:9">
<text y="203" x="36">20</text>
<text y="183" x="36">40</text>
<text y="163" x="36">60</text>
<text y="143" x="36">80</text>
<text y="123" x="36">100</text>
<text y="103" x="36">120</text>
<text y="83" x="36">140</text>
<text y="63" x="36">160</text>
<text y="43" x="36">$180</text>
</g>
<path style="stroke-width:2; stroke:black; fill:green" d="M 20 220 L 20
75.80000000000001 L 40 75.80000000000001 L 40 220 Z"/>
<text style="font-size:10; text-anchor:middle" x="30"
y="70.80000000000001">144.2</text>
<text x="240" style="font-size:14; text-anchor:start"
y="60">Southeast</text>
<path style="stroke:black; stroke-width:2; fill:green" d="M 220 50 L 220 60
L 230 60 L 230 50 Z"/>
<path style="stroke-width:2; stroke:black; fill:green" d="M 20 220 L 20
116.2 L 40 116.2 L 40 220 Z"/>
<text style="font-size:10; text-anchor:middle" x="30" y="111.2">103.8</text>
<text x="240" style="font-size:14; text-anchor:start"
y="60">Northeast</text>
<path style="stroke:black; stroke-width:2; fill:green" d="M 220 50 L 220 60
L 230 60 L 230 50 Z"/>
<path style="stroke-width:2; stroke:black; fill:green" d="M 20 220 L 20
133.7 L 40 133.7 L 40 220 Z"/>
<text style="font-size:10; text-anchor:middle" x="30"
y="128.7">86.30000000000001</text>
<text x="240" style="font-size:14; text-anchor:start"
y="60">Southwest</text>
<path style="stroke:black; stroke-width:2; fill:green" d="M 220 50 L 220 60
L 230 60 L 230 50 Z"/>
<path style="stroke-width:2; stroke:black; fill:green" d="M 20 220 L 20
171.1 L 40 171.1 L 40 220 Z"/>
<text style="font-size:10; text-anchor:middle" x="30" y="166.1">48.9</text>
<text x="240" style="font-size:14; text-anchor:start" y="60">Midwest</text>
<path style="stroke:black; stroke-width:2; fill:green" d="M 220 50 L 220 60
L 230 60 L 230 50 Z"/>
<path style="stroke-width:2; stroke:black; fill:green" d="M 20 220 L 20
198.6 L 40 198.6 L 40 220 Z"/>
<text style="font-size:10; text-anchor:middle" x="30"
y="193.6">21.400000000000002</text>
<text x="240" style="font-size:14; text-anchor:start"
y="60">Northwest</text>
<path style="stroke:black; stroke-width:2; fill:green" d="M 220 50 L 220 60
L 230 60 L 230 50 Z"/>
</svg>
Received on Monday, 16 July 2001 11:28:38 GMT

This archive was generated by hypermail 2.3.1 : Friday, 8 March 2013 15:54:20 GMT