NewtFire logo: a mosaic rendering of a firebelly newt
newtFire {dh|ds}
Maintained by: Elisa E. Beshero-Bondar (ebb8 at pitt.edu) Creative Commons License Last modified: Sunday, 06-Aug-2017 18:16:25 EDT. Powered by firebellies.

adapted with small revisions from Obdurodon.

The text

With this assignment, you will gain experience with writing XSLT to generate SVG. We will work with an XML document from a Pitt student project on Lewis Carroll’s Alice in Wonderland, and you can download the XML file from here by right-clicking on this. (Don’t just click to open it in a browser and copy, which can add some browser rendering characters that will mess up your code; right click and download.)

Let’s analyze the Alice XML file. The root element, <alice>, contains <cast> and <titlePage> child elements, both of which you can ignore, followed by twelve child <chapter> elements. The chapters are numbered with a @which attribute, e.g., <chapter which="1">. Chapters contain paragraph elements (<p>), which contain various child elements and descendants. You are interested in the <q> elements inside the chapters (at various depths), which are used to tag quotes by characters in the story.

The task

Your goal is to create a line graph that charts the number of quotes by Alice herself in each chapter. You do not have to graph any character except Alice. Your X axis marks the chapters and your Y values reflect the number of <q> elements in each chapter that have an @sp attribute equal to alice. A complete line graph might look something like http://newtfire.org/dh/alice_svg_output.svg. We are going to plot a line graph (instead of something like a bar graph) because we want to indicate variation across a connected series (over time) from chapter to chapter. (When you think about SVG for your projects, think about what kinds of plots make sense: For some kinds of data that aren't connected to each other, we might want side-by-side bars to compare.)

The XSLT Stylesheet Template for Outputting SVG

To output SVG from XML, we need to make some modifications to the default xs:stylesheet and xsl:output statements, so that the XSLT properly outputs valid W3c code that we can read in a web browser. Below is the code you need at the top of your XSLT file for this assignment. The input XML is not in the TEI namesepace, so we use the defalut XML schema, but we need to indicate the output SVG namespace (in the highlighted purple line below. The xsl:output method is set to "xml" as a default, classifying SVG as a kind of XML.

           <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    xmlns="http://www.w3.org/2000/svg">
   
    <xsl:output method="xml" indent="yes"/> 
            
        

Working with variables

Our solution uses global variables (variables that are defined once for the entire stylesheet) as well as variables that have different values for each chapter in the book (that is, for each dot in the graph). We worked with variables already in the XSLT Exercises 5 and 6, but for a review, see http://www.w3schools.com/xsl/el_variable.asp or look them up in the Michael Kay book. What is new here is that we are going to use multiple variables, and most of these will help us to generate the number values we need to plot our SVG graph.

As an example of a global variable, the amount of space between chapters on the X axis (that is, the amount of horizontal space between dots) is constant for the entire SVG graph. That is, each dot is the same distance from its neighbors as other dots are from their neighbors. If we want that distance to be, say, 100 pixels, we can define a global variable with something like:

<xsl:variable name="Xinterval" select="100"/>

We can then refer to the $Xinterval variable when we need to space out our dots while plotting them. This is a convenience variable, which means that it wasn’t absolutely necessary, since we could instead have written 100 wherever we need it. What’s convenient about the variable is that if we later decide to change the value, it could be hard to find a number inside XSLT or SVG code. If we’ve put the number in a variable definition, we can find and change it more easily. Global variables are defined as children of the root <xsl:stylesheet> element. We usually write them immediately after our <xsl:output> element, so that we can find them easily.

We can also set convenience variable values that may be different for different chapters (different dots). These are not global variables because they don’t always have the same value. For example, in the code that draws the dots for each chapter, we can set the X position of a dot with something like:

<xsl:variable name="Xpos" select="position() * $Xinterval"/>
using the position() function to return the position of each chapter, or just use the number value coded in the @which attribute:
<xsl:variable name="Xpos" select="@which * $Xinterval"/>

This is using the symbol for multiplication (*). We can use the following basic numeric operators in XPath and XSLT: + (for adding one value to another), - (for subtracting one value from another), and * (for multiplying one value by another). For division, we cannot use the forward slash because that literally means taking an XPath step, so we use div (to divide one value by another), and one more: mod to return the remainder after dividing one value by another. (Here is a handy page to look at examples of each notation.) In our example above, if we’ve previously defined $Xinterval as equal to 100, our code for multiplication will set the value of the $Xpos variable to 100 for chapter 1 (the first of the twelve chapters, and therefore in position #1), to 200 for chapter 2, etc. (You will also calculate the Y position and assign that value to the variable $Ypos.) We can then plot the dot with something like:

<circle cx="{$Xpos}" cy="{$Ypos} r="5" fill="red">

Note the curly braces, which create an attribute value template (AVT) that causes the variable to be interpreted and its value to be output. (If you don’t use an AVT, you’ll set your X position to a literal value of $Xpos, which is invalid because the X position of an SVG <circle> element must be numeric.) You don’t need curly braces for the @r attribute (the radius of the circle) if you just plug in a literal number for it like we did here, and you don’t need it for the @fill (color) because that value should be a literal color name or some other representation of a color. If you want to, though, you could store either of these values in global variables and call on them here, too, using curly braces.

Putting the X position into a variable is handy because you're going to need it both to position the dot and to position the chapter label (see the labels on the X axis on our sample output at the link above). If you calculate the position for each chapter once and stash it in a variable, you reuse the variable to position two things without having to redo the calculation.

Writing the XSLT

Writing SVG with XSLT almost always involves using the “pull” approach, which selects for just the bits of data you need. After you create the SVG superstructure, plot the X and Y axes, and label the Y axis at intervals, you can draw the dots, the lines between dots, and the labels on the X axis either by applying templates to all <chapter> elements or by using <xsl:for-each select="//chapter">. If you use the <xsl:for-each> strategy, you’ll need only one template for the document node, and you’ll put the <xsl:for-each> inside that. If you apply templates to chapters, you’ll need two templates: a template for the document node, in which you’ll create the SVG superstructure, and a template that matches <chapter> elements.

You can draw the dot for each chapter when you process that chapter, but to draw connecting lines between the dots, you’ll need to access information about two chapters at once. One will be the one you’re processing; the other will be either the one before or the one after. There are several ways to do that, and we’ll talk about them when we go over the solution, but whatever you do, note that with twelve chapters you have twelve dots but only eleven connecting lines. This means that one chapter (either the first or the last, depending on how you structure your code) will have to be treated differently from the others. For that reason, you may find it useful to test whether you’re at the beginning or end of the sequence of chapters with <xsl:if>.

Don’t forget to title your plot and add markers on your X and Y axes so your graph is human-readable!

Bonus Challenge

Once we generate the line graph plotting the number of times Alice speaks in each chapter, we might want to see how those values compare with the number of speeches by all the other characters combined, that is, speaking parts by everyone who is not Alice. Our solution uses the not() function to collect all the non-Alice speeches. We plotted a second line graph to superimpose on the first so we could compare the two sets of data, and chose different colors for the two lines. Our sample output plot looks like this, but you should improve on it by adding a title and a legend to finish labeling the graph. compares that of all the with all the other characters besides Alice,

What to Submit

Turn in your XSLT file. We will generate your SVG output ourselves by running your XSLT. Remember to save and open your SVG output in oXygen and in a web browser to be sure it is valid and that it is rendering as you think it should be.