Simon Potter simon.potter@auckland.ac.nz and
              Paul Murrell p.murrell@auckland.ac.nz
            
Department of Statistics, University of Auckland
March 29, 2013
                Abstract: The gridSVG package exports
                grid images to the SVG image format
                for viewing on the web. This article describes the
                problems associated with retaining grid
                object names in SVG element id
                attributes. In addition, new features
                in gridSVG that allow manipulation and
                retrieval of generated id attributes are
                discussed. These new features allow for easier and
                more predictable development of interactivity in plots
                generated by gridSVG.
            
grid is an alternative graphics system to the traditional base graphics system provided by R [1]. Two key features of grid distinguish it from the base graphics system: graphics objects and viewports.
Viewports are how grid defines a drawing context and plotting region. All drawing occurs relative to the coordinate system within a viewport. Viewports have a location and dimension and set scales on the horizontal and vertical axes. Crucially, they also have a name so we know how to refer to them.
          Graphics objects (grobs) store information necessary to describe
          how a particular object is to be drawn. For example,
          a grid circleGrob contains the information
          used to describe a circle, in particular its location and its
          radius. As with viewports, graphics objects also have names.
        
The task that gridSVG [2] performs is to translate viewports and graphics objects into SVG [3] equivalents. In particular, the exported SVG image retains the naming information on viewports and graphics objects. The advantage of this is we can still refer to the same information in grid and in SVG. This means that interactivity can be added to specific named graphics objects to do things like adding tooltips or highlighting a point. In addition, we are able to annotate grid grobs to take advantage of SVG features such as hyperlinking and animation.
          The fact that SVG is
          an XML-based [4] image format
          means that if we are to identify SVG output by
          name, we are required to
          produce SVG id attributes that are
          unique. This document describes how gridSVG retains
          the names associated with grobs and viewports, along with
          the difficulties in doing so.
        
          When gridSVG exports the grid display
          list, it attempts to give SVG id
          attributes the same value as the name associated with a grob
          or viewport. However, the fact we require a
          unique id presents us with problems in
          maintaining these names for a few reasons, which will be
          discussed later. For now, we will first look at an image
          drawn in grid and what gridSVG produces
          from that grid scene.
        
A simple image will be drawn where we have two viewports and a circle is then drawn inside those viewports. The code to produce that image and the display list that grid records for the image are shown below:
R> pushViewport(viewport(name = "a")) R> pushViewport(viewport(name = "b", width = 0.5, height = 0.5)) R> grid.circle(name = "a", gp = gpar(fill = "steelblue")) R> grid.ls(viewports = TRUE, fullNames = TRUE)
viewport[ROOT]
  viewport[a]
    viewport[b]
      circle[a]
          What we can see is that there are three viewports,
          named ROOT, a, and b,
          one of which shares its name with a circle
          called a. The ROOT viewport is a
          viewport that grid creates by default that
          corresponds to the entire drawing canvas. This explains
          why ROOT exists in the display list without
          explicitly being created.
        
          Ideally we would like to see that grid's viewport
          and grob names are mapped directly
          to SVG id attributes. However, because
          we are constrained to having our SVG
          element id attributes being
          unique, gridSVG must take action to ensure this is
          the case. Before explaining how gridSVG does this,
          let us first consider the simple example we just created by
          examining the relevant output from gridSVG.
        
<g id="a.1">
  <g id="a::b.1">
    <g id="a.2">
      <circle id="a.2.1" cx="252" cy="252" r="126" fill="rgb(70,130,180)" fill-opacity="1"/>
    </g>
  </g>
</g>
          We see here that none of the names we have
          in grid are mapped directly
          to SVG id attributes. The
          grid names are still being retained, albeit
          modified from the original names. The following name
          translations occurred:
        
a became
            an SVG <g> element
            whose id attribute is a.1.
          b became
            an SVG <g> element
            whose id attribute is a::b.1.
          a became
            an SVG <g> element with
            an id attribute
            of a.2. This <g> element
            has a child <circle> element
            whose id attribute is a.2.1.
          This name translation is clearly evident. How gridSVG performs this translation will now be discussed.
In grid, both grobs and viewports can be constructed as a tree of viewports or a tree of grobs. To find a particular viewport or a grob within a tree, we need to use a path. This path is an ordered list of names, specifying parent-child relations. We will be focusing on viewport paths for simplicity, but the same principle applies to trees of graphics objects.
An example of a viewport path is shown below:
R> vpPath("first", "second", "third")
first::second::third
          This viewport path describes that we first visit the
          viewport called first, followed by its
          child, second. Once in the second
          viewport, we then traverse to its child
          viewport third. We can see that the resulting
          path is simply a double-colon separated string of names.
        
It is possible to create a path where not all names in the path are unique.
R> # Creating viewports, note vp1 and vp3 have the same name R> vp1 <- viewport(name = "a") R> vp2 <- viewport(name = "b") R> vp3 <- viewport(name = "a", width = 0.5) R> R> # Creating a tree of viewports R> vpT <- vpTree(vp1, vpList(vpTree(vp2, vpList(vp3)))) R> print(vpT)
viewport[a]->(viewport[b]->(viewport[a]))
R> R> # Pushing into the tree R> pushViewport(vpT) R> R> # Showing our current viewport path R> current.vpPath()
a::b::a
          In this example we create a viewport tree and push into
          it. We then observe our current viewport path to
          be a::b::a. Despite there being
          two a viewports in the path, they are each in
          fact two completely different viewports. As a result, we
          cannot simply assign the name of each viewport in the path
          to SVG output because the id attribute may not
          be unique. In our simple example, if we were to do this, we
          would end up with two SVG elements
          named a. The relevant output showing the result
          of this is shown below:
        
<g id="a">
  <g id="b">
    <g id="a"/>
  </g>
</g>
          This demonstrates that names alone are not sufficient for
          the requirement of unique id attributes. A
          potential solution is to use the path as the name
          of the element. The path would avoid
          repeating a in id attributes. This
          would produce output like the following:
        
<g id="a">
  <g id="a::b">
    <g id="a::b::a"/>
  </g>
</g>
          This looks like an adequate solution as we have produced
          unique id attributes, despite having viewports
          with the same names. However, because viewports can be moved
          in and out of at any point, we cannot guarantee that the
          viewport tree is fixed while the plot is being
          drawn. Consider the following:
        
R> pushViewport(vpT) R> current.vpPath()
a::b::a
R> upViewport() R> current.vpPath()
a::b
R> pushViewport(viewport(name = "a", height = 0.1)) R> current.vpPath()
a::b::a
          What is happening here is that we first push into our tree
          but then navigate back to the previous viewport path
          of a::b. A new viewport is then created
          called a, and we push into that viewport
          instead of the viewport that we were previously in. This
          creates an ambiguity because we have two different viewports
          that have been pushed into at the same path
          of a::b::a.
        
<g id="a">
  <g id="a::b">
    <g id="a::b::a"/>
    <g id="a::b::a"/>
  </g>
</g>
          We can see here that despite using paths, they are not
          sufficient for uniqueness when generating an
          SVG id attribute. This problem is also present
          when revisiting the same viewports in a viewport path. To
          overcome this problem, we use an integer suffix that is
          incremented each time we encounter the same path. To ensure
          consistency, this integer suffix is applied to every
          path. The result is shown below:
        
<g id="a.1">
  <g id="a::b.1">
    <g id="a::b::a.1"/>
    <g id="a::b::a.2"/>
  </g>
</g>
          What we can see here is that we visit the top a
          for the first time. We then traverse to the viewport
          path a::b for the first time. It is important
          to note however that we can see that we have traversed
          to a::b::a on two separate occasions.
        
          By keeping track of viewport and grob paths we can ensure
          that their SVG id attributes are unique. In
          addition, their uniqueness allows us to easily retain
          viewport coordinate information (see 'Working with the
          gridSVG Coordinate System' [5]),
          because the coordinate information will be paired with
          the SVG id that was generated by
          gridSVG. This is necessary because each time a
          viewport path is visited there may be a different coordinate
          system in use. For example, consider the case where we
          create two different viewports share the same name but use
          different coordinate systems:
        
R> a1 <- viewport(width = 0.5, height = 0.5, name = "a") R> a2 <- viewport(width = 0.1, height = 0.1, name = "a") R> pushViewport(a1) R> current.vpPath()
a
R> popViewport() R> pushViewport(a2) R> current.vpPath()
a
R> popViewport()
          Firstly, two viewports with the name a have
          been created. These two viewports have a different width and
          height to one another. This is important because each time
          the viewports are used the viewport path is the same despite
          different coordinate systems being used. The id
          attributes that that gridSVG will generate in this
          situation should now be familiar:
        
<g id="a.1"/> <g id="a.2"/>
          The unique id attributes of a.1
          and a.2 allow us to look up coordinate
          information based on these generated names. To see this in
          action, we only need to look at the relevant subset of
          coordinate information that has been exported to
          JSON [6], a structured data format.
        
{
	"a.1" : {
		"x" : 126,
		"y" : 126,
		"width" : 252,
		"height" : 252,
		"xscale" : [
			0,
			1
		],
		"yscale" : [
			0,
			1
		],
		"inch" : 72
	},
	"a.2" : {
		"x" : 226.8,
		"y" : 226.8,
		"width" : 50.4,
		"height" : 50.4,
		"xscale" : [
			0,
			1
		],
		"yscale" : [
			0,
			1
		],
		"inch" : 72
	}
}
          This clearly illustrates that the viewport coordinate
          systems for both viewports has been retained. We know this
          is the case because
          the x, y, width
          and height attributes for the viewports are
          indeed different. By generating unique id
          attributes for viewport paths, we can guarantee that
          coordinate information is not only retained, but is also
          unambiguous.
        
In grid, both viewports and grobs contain names. Indeed, we have seen they can also be referred to by a path. One problem that gridSVG has that grid isn't concerned with is that viewports and grobs can have the same name. Consider the following example:
R> pushViewport(viewport(name = "a")) R> grid.circle(name = "a") R> grid.ls(viewports = TRUE, fullNames = TRUE)
viewport[ROOT]
  viewport[a]
    circle[a]
          We can see that a viewport has a name that is the same as a
          circle grob's name. grid is able to draw this scene
          without any issues but we are presented with a problem when
          exporting it using gridSVG. We want the
          SVG id attribute to be assigned with
          the name of the object that we're representing. However, in
          this example, because this is the first time each grob path
          and each viewport path is encountered, we can end up with
          non-unique elements. This is shown below:
        
<g id="a.1">
  <g id="a.1">
    <circle cx="252" cy="252" r="252"/>
  </g>
</g>
          The reason why both the viewport and circle grob are given
          the suffix of .1 is because it is both the
          first time that the viewport path has been visited and it is
          the first time that the grob path has been visited. This
          presents us with a case where id attributes
          between grobs and viewports are shared. To correct this, we
          not only need to track paths, but we also need to track the
          names and how often they have been assigned to both
          viewports and grobs. The solution currently used
          by gridSVG is shown below:
        
<g id="a.1">
  <g id="a.2">
    <circle cx="252" cy="252" r="252"/>
  </g>
</g>
          Now instead of just tracking how often each viewport or grob
          path has been used, we track how often each grid
          name has been used. This is shown in our output because when
          the circle grob is drawn, it is the second time that the
          name a has been encountered, so we end up with
          a suffix of .2.
        
          In summary, by tracking the names that we attempt to apply
          to id attributes, we ensure that unique
          id attributes are generated by gridSVG
          by adding an integer suffix.
        
          We have already seen that when a grob is drawn, we create
          an SVG <g> element. The contents
          of this grouping element are graphical elements
          (e.g. rectangles, circles, lines) that are drawn to
          an SVG canvas. The reason why grouping output is
          necessary is because there are cases where gridSVG
          cannot create a one-to-one mapping between a grid
          grob and SVG output. For example, while it is
          possible for a single grid circle to be drawn
          simply as an SVG <circle />
          element, we cannot assume this to always be true. We can
          draw multiple circles using a single call
          to grid.circle. An example of this is shown
          below:
        
R> grid.circle(r = 1:3 / 10, name = "a") R> grid.ls()
a
A single circle grob (as listed on the display list) has managed to draw three separate circles. These will be referred to as sub-grobs. It is clear that we cannot apply any name given to the grob to all of its sub-grobs because all sub-grobs would therefore have identical names. The solution gridSVG uses is to use an integer suffix to identify each sub-grob that is drawn. Using the example above, we will take a look at the SVG output that gridSVG produces.
<g id="a.1"> <circle id="a.1.1" cx="252" cy="252" r="50.4"/> <circle id="a.1.2" cx="252" cy="252" r="100.8"/> <circle id="a.1.3" cx="252" cy="252" r="151.2"/> </g>
          Firstly, we can see that the original grob name has been
          changed to a.1 because it is the first time
          that we use the name a. However, its children
          also have an integer suffix applied. The first circle drawn
          (i.e. the circle with a radius of 0.1) is given the
          name a.1.1. The second and third circles are
          assigned the names a.1.2 and a.1.3
          respectively.
        
          This technique also applies to grobs where there is
          an id parameter. An example of such a grob is
          a polylineGrob.
        
R> grid.polyline(x = c(0:4 / 10, rep(.5, 5), 10:6 / 10, rep(.5, 5)), R+ y = c(rep(.5, 5), 10:6 / 10, rep(.5, 5), 0:4 / 10), R+ id = rep(1:5, 4), R+ gp = gpar(col = 1:5, lwd = 3), R+ name = "a")
          A single call to grid.polyline has produced 5
          distinct lines. This is because
          of grid.polyline's id parameter
          which determines the sub-grob that each line coordinate
          belongs to. The SVG output is shown below, and
          demonstrates that the same rule applies to grobs with
          vectorised parameters and to those with an id
          parameter.
        
<g id="a.1"> <polyline id="a.1.1" ... /> <polyline id="a.1.2" ... /> <polyline id="a.1.3" ... /> <polyline id="a.1.4" ... /> <polyline id="a.1.5" ... /> </g>
          The addition of an integer suffix to sub-grobs allows us to
          not only generate unique ids for SVG
          elements, but also allows us to identify each sub-grob that
          is being drawn in a consistent manner.
        
          This article has shown why gridSVG needs to modify
          names to produce unique output. One of the problems in doing
          this is that the SVG id attributes are
          now much harder to predict. This means that any name that is
          assigned to a grob or viewport in grid, when
          exported to SVG by gridSVG, does not map
          to an easily predictable SVG id
          attribute. However, to aid predictability, gridSVG
          does offer some options for controlling how it
          constructs id attributes.
        
usePaths Option
          It was discussed earlier why viewport paths are used as part
          of the exported ids. However, there are cases
          where this unnecessarily complicates the SVG
          output. Primarily this is the case when the names of each
          viewport — and therefore every viewport path —
          are unique. Using viewport paths as part of the
          generated id attributes is therefore not
          strictly necessary. We add the complication of dealing with
          paths when our viewport names are sufficiently specific.
        
          The usePaths parameter
          for gridSVG's gridToSVG function
          allows us to determine whether paths are used when
          creating ids for grobs and viewports. There are
          four possible options:
        
vpPaths: Use paths
          in SVG ids for viewport paths. This is
          the default behaviour because it makes coordinate system
          exporting clearer as there are likely to be fewer name
          conflicts.gPaths: Use paths in SVG ids for grob paths.none: Do not use paths for either viewport paths or grob paths.both: Generate paths for both viewport paths and grob paths.To demonstrate the effect of these options, a simple image will be drawn, then we will examine the relevant SVG output that gridSVG generates from each option.
R> # Create viewports and grobs R> vpa <- viewport(name = "a") R> vpb <- viewport(name = "b", width = 0.5, height = 0.5) R> rectc <- rectGrob(name = "c") R> circd <- circleGrob(name = "d") R> R> # Construct trees of the viewports and grobs R> vpt <- vpTree(vpa, children = vpList(vpb)) R> gt <- gTree(children = gList(rectc, circd), name = "gt") R> R> # Draw the image R> pushViewport(vpt) R> grid.draw(gt) R> R> # Examine what grid sees R> grid.ls(viewports = TRUE, fullNames = TRUE)
viewport[ROOT]
  viewport[a]
    viewport[b]
      gTree[gt]
        rect[c]
        circle[d]
          What has been drawn are two grobs, a circle and a
          rectangle. They are the only children in a single tree of
          grobs called gt. This tree has been drawn
          inside the viewport path a::b. Because we have
          trees of content, we can easily compare the effect of each
          option. We will first look at the output when we only want
          viewport paths to be used.
        
R> gridToSVG(usePaths = "vpPaths")
<g id="a.1">
  <g id="a::b.1">
    <g id="gt.1">
      <g id="c.1">
        <rect id="c.1.1" x="126" y="126" width="252" height="252"/>
      </g>
      <g id="d.1">
        <circle id="d.1.1" cx="252" cy="252" r="126"/>
      </g>
    </g>
  </g>
</g>
          Only viewport paths are being used here. As a result each of
          the grob names are kept unchanged and instead of viewport
          names we use the viewport path. We know this is the case
          because there is an id that has been exported
          that can only belong to a viewport
          path, a::b.1. In addition, both
          the c and d grobs do not use paths
          for their names so are exported as c.1
          and d.1 respectively.
        
R> gridToSVG(usePaths = "gPaths")
<g id="a.1">
  <g id="b.1">
    <g id="gt.1">
      <g id="gt.1::c.1">
        <rect id="gt.1::c.1.1" x="126" y="126" width="252" height="252"/>
      </g>
      <g id="gt.1::d.1">
        <circle id="gt.1::d.1.1" cx="252" cy="252" r="126"/>
      </g>
    </g>
  </g>
</g>
          The viewports are now being retained as just being the
          names, while we are now using paths for grobs. The viewport
          path a::b is consequently exported simply
          to b.1, which can only be the case if we ignore
          the path prefix of a. The grob path is being
          used in particular with the rectangle and circle grobs
          because they are now being exported with
          the ids of gt.1::c.1
          and gt.1::d.1 respectively. This clearly
          indicates that they are children of the gTree
          named gt.
        
R> gridToSVG(usePaths = "none")
<g id="a.1">
  <g id="b.1">
    <g id="gt.1">
      <g id="c.1">
        <rect id="c.1.1" x="126" y="126" width="252" height="252"/>
      </g>
      <g id="d.1">
        <circle id="d.1.1" cx="252" cy="252" r="126"/>
      </g>
    </g>
  </g>
</g>
          No paths are being used so we are only exporting the names
          of the viewports and grobs. This is particularly evident
          because the default path separator of :: is no
          longer present in any of our id attributes.
        
R> gridToSVG(usePaths = "both")
<g id="a.1">
  <g id="a::b.1">
    <g id="gt.1">
      <g id="gt.1::c.1">
        <rect id="gt.1::c.1.1" x="126" y="126" width="252" height="252"/>
      </g>
      <g id="gt.1::d.1">
        <circle id="gt.1::d.1.1" cx="252" cy="252" r="126"/>
      </g>
    </g>
  </g>
</g>
          Finally, we observe the output created by
          exporting ids as both viewport paths and grob
          paths.
        
          When gridSVG exports paths
          as SVG ids, the result is that each
          name in the path is separated by ::. This is
          the default path separator used by grid. However,
          there may be situations where a custom path separator may be
          more appropriate. An example where this is the case is when
          using ids within CSS
          selectors [7]. This is because the
          colon character is a special character in
          CSS [8], as it prefixes a
          pseudo-selector. Therefore, if we were to use the
          default gridSVG path separator, we would need
          to escape it for use within a CSS selector. This
          would require modifying each instance of :: and
          replacing it with \:\:. Ideally we would like
          to avoid performing any escaping by using a different
          separator. This section discusses how custom separators can
          be by gridSVG when it exports an SVG
          image.
        
There are three types of separators that gridSVG uses:
vpPath: The separator used between names in
          a viewport path. The default value is ::.gPath: The separator used between names in
          a grob path. The default value is ::.id: The separator between the name given to
          a grob or viewport and and additional integer suffix, used
          for the purposes of ensuring uniqueness. By default this
          value is .. This also applies to sub-grobs and
          additional SVG output like clipping paths and
          marker names.
          We can change the values of these separators, avoiding the
          need to escape them for use within CSS selectors. Another
          possible reason why using custom separators might be useful
          is if we have grob names containing .
          characters. By changing the id separator, we
          can make it easier to determine the grob or viewport name
          from the generated SVG id attribute.
        
          gridSVG provides three functions that are useful
          for the purposes of changing the separators used when
          generating SVG id
          attributes: setSVGoptions, getSVGoptions,
          and getSVGoption. setSVGoptions
          allows us to change the separators,
          while getSVGoptions allows us to
          query gridSVG for all current
          separators. getSVGoption is a convenience
          function that gives us the value of a single
          separator. Example usage is shown below:
        
R> # See what the current id separator is
R> getSVGoption("id.sep")
[1] "."
R> # Now let's see all of them R> getSVGoptions()
$id.sep [1] "." $gPath.sep [1] "::" $vpPath.sep [1] "::"
R> # Set new separators R> setSVGoptions(vpPath.sep = "_", R+ gPath.sep = "_", R+ id.sep = "-")
          Now that we have changed the separators, we can examine the
          effect of these changes by drawing our earlier example again
          with usePaths being set
          to "both". The relevant output is shown below:
        
R> pushViewport(vpt) R> grid.draw(gt)
R> gridToSVG(usePaths = "both")
<g id="a-1">
  <g id="a_b-1">
    <g id="gt-1">
      <g id="gt-1_c-1">
        <rect id="gt-1_c-1-1" x="126" y="126" width="252" height="252"/>
      </g>
      <g id="gt-1_d-1">
        <circle id="gt-1_d-1-1" cx="252" cy="252" r="126"/>
      </g>
    </g>
  </g>
</g>
R> # Resetting to defaults R> setSVGoptions(vpPath.sep = "::", gPath.sep = "::", id.sep = ".")
          Notice how the each of the grob and viewport paths now have
          underscore characters in them. Additionally,
          every id now has a dash as a separator to the
          integer suffix.
        
          By default, to ensure valid SVG content, gridSVG
          adds an integer suffix for the purposes of making the
          generated id attribute unique. A consequence of
          this is that there is not a one-to-one mapping
          between grid names
          and SVG ids. This makes it hard to
          predict the SVG id that is generated
          for a grob or viewport, presenting challenges when we want
          to use the SVG output. For example, in JavaScript,
          if we want to change the colour of a grob as we hover our
          mouse over it, we first need to know the id of
          the SVG element that we are applying this effect
          to.
        
          If a grid plot has been drawn that is known to have
          unique grob and viewport names, this procedure of adding an
          integer suffix is not required. gridSVG provides an
          option for enabling this process, uniqueNames,
          which is TRUE by default. In the case when this
          parameter is FALSE it is possible to produce
          valid SVG without the addition of any integer
          suffixes. This means that we can create a one-to-one mapping
          between grid grob names and the id
          attributes that gridSVG generates. This parameter
          only affects grob names because modifying viewport names
          could affect retention of coordinate information. A simple
          demonstration of the effect of uniqueNames is
          shown below:
        
R> grid.circle(name = "circle") R> grid.ls()
circle
R> gridToSVG(uniqueNames = FALSE)
<g id="circle"> <circle id="circle.1" cx="252" cy="252" r="252"/> </g>
          We can see that the id generated for the grob
          named circle is still circle. One
          important thing to note is that gridSVG does not
          change its behaviour for sub-grobs. This is why
          the <circle /> element has
          an id of circle.1.
        
          When the uniqueNames argument is set
          to FALSE, it is possible to generate
          invalid SVG. This may occur when grobs and/or
          viewports share names when exported
          to SVG. gridSVG will generate non-unique
          names, but it will provide a warning in this case because
          invalid SVG is being produced. See the following:
        
R> # Giving a rect, and a viewport the same "name" when exported R> pushViewport(viewport(name = "a")) R> grid.rect(name = "a.1") R> grid.ls(viewports = TRUE, fullNames = TRUE)
viewport[ROOT]
  viewport[a]
    rect[a.1]
R> gridToSVG(uniqueNames = FALSE)
Warning: Not all element IDs are unique. Consider running 'gridToSVG' with 'uniqueNames = TRUE'.
<g id="a.1">
  <g id="a.1">
    <rect id="a.1.1" x="0" y="0" width="504" height="504"/>
  </g>
</g>
          In this example, gridSVG is not checking whether
          the id a.1 already exists. The
          viewport is given the expected gridSVG name
          of a.1 because it is the first time that
          the a viewport path has been pushed into. Now
          when we come across a grob called a.1, no
          checking is occurring to see whether the id
          already exists. Additionally,
          because uniqueNames is set
          to FALSE, no integer suffix is added for the
          purpose of ensuring uniqueness. Therefore we end up with
          two id attributes that are the same, creating
          invalid SVG, which gridSVG is providing a
          warning message for.
        
          Care should be taken when using this parameter because it is
          the only parameter which has the potential to produce
          invalid SVG documents. In fact, the need to change
          this parameter from the default of TRUE is
          rarely necessary when we use mapping information.
        
          We have shown how gridSVG can be used to modify its
          resulting SVG output. These settings can be particularly
          useful for deriving the structure of a source image. For
          example, we might like to know whether an id
          attribute is part of a grob path or a viewport path. If we
          know the values of separators used when exporting, we can
          make more sense of a gridSVG generated SVG
          document. This is also particularly useful for debugging
          a gridSVG image.
        
Settings used for controlling SVG output are exported as metadata in the SVG that gridSVG creates. To demonstrate this, we will draw a simple grid image (not shown), but show only the SVG metadata that was exported by gridSVG.
<metadata xmlns:gridsvg="http://www.stat.auckland.ac.nz/~paul/R/gridSVG/"> <gridsvg:generator name="gridSVG" version="1.0-0" time="2013-03-29 09:08:21"/> <gridsvg:argument name="name" value=""/> <gridsvg:argument name="export.coords" value="none"/> <gridsvg:argument name="export.mappings" value="none"/> <gridsvg:argument name="export.js" value="none"/> <gridsvg:argument name="res" value="72"/> <gridsvg:argument name="indent" value="TRUE"/> <gridsvg:argument name="htmlWrapper" value="FALSE"/> <gridsvg:argument name="usePaths" value="vpPaths"/> <gridsvg:argument name="uniqueNames" value="TRUE"/> <gridsvg:separator name="id.sep" value="."/> <gridsvg:separator name="gPath.sep" value="::"/> <gridsvg:separator name="vpPath.sep" value="::"/> </metadata>
          The metadata shows us exactly how the image was drawn. In
          particular, the gridsvg:argument elements which
          tell us how the id attributes were
          controlled. We can see that as this particular image was
          exported it was ensuring that unique names were being used
          and the only paths it was generating were for viewport
          paths. Additionally, we can also see the values of the
          separators when the image was being exported.
        
          We have discussed the many ways in which gridSVG
          modifies grob and viewport names, including ways to control
          how that happens. However, the key issue with this name
          translation is that it is difficult to predict how to map
          the names that are used in grid with the output
          produced by gridSVG. A recent development
          in gridSVG is the ability to retain mapping
          information that provides us with information on how to map
          a grid grob or viewport name to
          an SVG id attribute.
        
          It is useful to have mapping information available both in
          R, and in JavaScript [9]. In R, we
          might want to perform some post-processing on the XML nodes
          that a grob maps to. If the id can be retrieved
          easily then performing this task is far simpler than writing
          an XPath [10] expression. Similarly, if
          we want to perform some modification on an SVG image in the
          browser, using tools like D3 [11],
          then knowing what content we're trying to select is an
          important problem to solve.
        
We will first look at the mapping information that gridSVG is exporting. We start with the following image:
R> grid.newpage() R> pushViewport(viewport(name = "a")) R> grid.rect(name = "b") R> grid.circle(name = "b")
R> gridToSVG()
This exports mapping information as JSON, a structured data format that is convenient for use within a web browser. The mapping information from this plot is shown below:
{
	"vps" : {
		"a" : {
			"suffix" : 1,
			"selector" : "#a\\.1",
			"xpath" : "//*[@id='a.1']"
		}
	},
	"grobs" : {
		"b" : {
			"suffix" : [
				1,
				2
			],
			"selector" : [
				"#b\\.1",
				"#b\\.2"
			],
			"xpath" : [
				"//*[@id='b.1']",
				"//*[@id='b.2']"
			]
		}
	},
	"refs" : null,
	"id.sep" : "."
}
          This is showing that we store both viewport and grob mapping
          information. Within each category, we store the name of the
          object, which has three pieces of information associated
          with it. The first are the integer suffixes that the name
          has been mapped to. For our example, we used the grob
          name b twice, so there are two suffixes
          associated with the b grob. This can be used to
          construct an id attribute by concatenating the
          name with the id.sep value.
        
          Also included are CSS selectors and XPath expressions which
          target the same id. These are included for
          convenience, and special characters are already
          escaped. This means that if we use a JavaScript library like
          D3 or jQuery [12], we can select the
          content immediately by just using the exported CSS selector.
        
          In order to make the mapping information easy to
          use, gridSVG provides convenience functions in both
          R and JavaScript. The primary function that is used
          is getSVGMappings, which is named the same in
          both R and JavaScript. To demonstrate, we will be
          building upon the mapping information shown earlier.
        
R> mappings <- readMappingsJS("mappings.js")
R> gridSVGMappings(mappings)
R> getSVGMappings("a", "vp") # getting 'a', which is a viewport
[1] "a.1"
R> getSVGMappings("b", "grob") # getting 'b', which are grobs
[1] "b.1" "b.2"
          The first thing that occurs is that because mapping
          information is stored in a file as JSON, we need to read it
          into R. The readMappingsJS function
          takes the filename containing mapping information, and reads
          that file into R and parses it as a list. The
          result can then be given
          to gridSVGMappings. Once this has been done we
          can apply the mapping information by
          using getSVGMappings.
        
          It is important to note that when a name has been used more
          than once, instead of getting a single id
          value, we can end up with multiple ids. This
          ambiguity cannot be resolved because of issues discussed
          earlier, but at least we can reduce the search to only
          the ids that have been returned from the
          function. Typically there are few instances where multiple
          results are returned.
        
The same example above can be performed in a browser using JavaScript, the output is shown below:
JS> getSVGMappings("a", "vp");
["a.1"]
JS> getSVGMappings("b", "grob");
["b.1", "b.2"]
        In this example, is it shown that the function always returns an array of values, even when there is only one matching result. This is for simplicity across single and multiple matching results.
          To return a CSS selector or XPath expression instead of
          an id we just need to specify that in the
          optional third parameter. Again, this is the case in both
          the R and JavaScript implementations of the
          function. This is shown below:
        
R> getSVGMappings("a", "vp", "selector")
[1] "#a\\.1"
R> getSVGMappings("a", "vp", "xpath")
[1] "//*[@id='a.1']"
R> getSVGMappings("b", "grob", "selector")
[1] "#b\\.1" "#b\\.2"
R> getSVGMappings("b", "grob", "xpath")
[1] "//*[@id='b.1']" "//*[@id='b.2']"
JS> getSVGMappings("a", "vp", "selector");
["#a\.1"]
JS> getSVGMappings("a", "vp", "xpath");
["//*[@id='a.1']"]
JS> getSVGMappings("b", "grob", "selector");
["#b\.1", "#b\.2"]
JS> getSVGMappings("b", "grob", "xpath");
["//*[@id='b.1']", "//*[@id='b.2']"]
        An example where this becomes useful is if you want to use D3 to modify content, perhaps using a transtition. All that is required is to get the appropriate selector and D3 can select the appropriate content based on that selector. For example the following shows how this might occur:
JS> var sel = getSVGMappings("a", "vp", "selector")[0];
JS> d3.select(sel)
JS+     .transition()
JS+     ...
        In R, the use of the XML package [13] is more familiar, so we can use XPath expressions instead of CSS selectors.
R> xp <- getSVGMappings("a", "vp", "xpath")
R> vpa <- getNodeSet(image, xp)[[1]]
R> vpa
<g id="a.1">
  <g id="b.1">
    <rect id="b.1.1" x="0" y="0" width="504" height="504"/>
  </g>
  <g id="b.2">
    <circle id="b.2.1" cx="252" cy="252" r="252"/>
  </g>
</g>
With the development of retaining name mapping information we can more easily manipulate SVG images that have been exported by gridSVG.
          We have demonstrated that grid grob and viewport
          names are required to be modified as they are translated
          to SVG id attributes. We have also
          shown why this is necessary and the process gridSVG
          takes to ensure valid SVG is generated.
        
          gridSVG also provides two parameters in
          its gridToSVG function which affect how
          modification of grob and viewport names occur. Despite the
          modification of names, it is straightforward to retrieve
          possible matching ids using convenience
          functions that access gridSVG's name mapping
          information.
        
          This document is licensed under a Creative Commons Attribution 3.0 New Zealand License.
           The code is freely available under
          the GPL. The described
          functionality of gridSVG is present in version
          1.1-0. gridSVG is currently under development
          on GitHub but
          will be migrated
          to R-Forge
          soon.
          The code is freely available under
          the GPL. The described
          functionality of gridSVG is present in version
          1.1-0. gridSVG is currently under development
          on GitHub but
          will be migrated
          to R-Forge
          soon.