Maintained by: David J. Birnbaum (djbpitt@gmail.com) Last modified: 2021-12-27T22:03:54+0000
This exercise uses an XML document from the Alice Project, which you can download 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.)
The root element of the Alice XML file, <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.
Your goal is to begin (just begin; see below) 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://dh.obdurodon.org/alice_svg_output_2151.svg.
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). If you aren’t already comfortable with XSLT variables, you need to begin by reading up on them at http://www.w3schools.com/xsl/el_variable.asp or in the Michael Kay book.
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"/>
If we’ve previously defined $Xinterval
as equal to 100
, this 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. We
can then plot the dot with something like (you will also already have calculated the Y
position and assigned that value to the variable $Ypos
):
<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) because that
value already is a number, 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.
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.
Although we have advised you to use the push model in all of your other homework,
generating SVG is a common situation where pull may be more convenient. 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 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>
.