XSL-Übersicht / Konvertierung von XML nach XML / Erzeugen von Skalierbaren Vektor Grafiken (SVG)

Erzeugen von Skalierbaren Vektor Grafiken (SVG)

Erzeugen von Skalierbaren Vektor Grafiken (SVG)

➪ Skalierbare Vektor Grafiken (SVG) sind pures XML mit einem speziellen Namespace, der die Funktionalitäten definiert, auf der u.a. auch Browser arbeiten, die SVG-Dateien anzeigen können. Spezielle Grafikelemente (Text, Rechteck, Kreis, Linie, ...) können mit Koordinaten und weiteren Eigenschaften ausgestattet werden, die ein Anzeigen als Grafik möglich machen.

Sehr sinnvoll ist der Einsatz von XSLT bei der Generierung von Skalierbaren Vektor Grafiken (SVG). Das ist zwar grundsätzlich auch unter XSLT 1.0 möglich; da in diesem Beispiel jedoch xsl:result-document sowie weitere Funktionen zur Anwendung kommen, die nur in XSLT 2.0 verfügbar sind, habe ich es hier aufgeführt.

Verhältnismäßig einfach ist das Erstellen von Balkendiagrammen. Dabei ist für das Verständnis wichtig, daß sich die Null-Koordinate nicht unten links auf einer Zeichenfläche, sondern oben links befindet.


<xsl:template name="generateSVG_Balkendiagramm">
  <xsl:result-document href="Balkendiagramm.svg">
    <xsl:variable name="vheight" 
        select="count(Orte/Ort/Mensch) * 15 + 130"/>
    <svg:svg 
         xmlns:svg="http://www.w3.org/2000/svg" 
         width="380" 
         height="{$vheight}" 
         style="fill:gray;">
      <svg:rect x="5" y="5" width="375" 
           height="{$vheight - 5}" 
           style="fill:none; stroke:black"/>
      <svg:text x="50" y="50">Einkommen</svg:text>	 
      <xsl:for-each select="Orte/Ort/Mensch">
        <xsl:sort select="Gehalt" 
             data-type="number" order="ascending"/>
        <svg:text x="20" y="{105 + position() *15}">
          <xsl:value-of select="vorname"/>
        </svg:text>
        <svg:rect 
             x="100" 
             y="{100 + position() *15}"
             width="{Gehalt * 0.04}"
             height="5"
             style="fill:blue"/>
      </xsl:for-each>		
    </svg:svg>
  </xsl:result-document>
</xsl:template>

Das Ergebnis sieht dann folgendermaßen aus:

pic/Balkendiagramm.svg

Schon etwas komplexer wird es bei der Generierung von Säulendiagrammen, da - wie bereits gesagt - die Null-Koordinate der Zeichenfläche sich oben links auf der Zeichenfläche befindet. Dadurch muß erst einmal eine Koordinatentransformation durchgeführt werden, um die Basislinie auf eine Ebene zu bekommen. Zu Darstellungszwecken ist es ggf. auch wünschenswert, die Textlegende in senkrechter Schrift zu realisieren.


<xsl:template name="generateSVG_Saeulendiagramm">
  <xsl:result-document href="Saeulendiagramm.svg">
   <xsl:variable name="zoomfaktor">0.05</xsl:variable>
   <xsl:variable name="vAbstandVonOben">20</xsl:variable>
   <xsl:variable name="vAbstandVonUnten">70</xsl:variable>
   <xsl:variable 
        name="vmaxGehalt" 
        select="max(Orte/Ort/Mensch/Gehalt)"/>
   <xsl:variable 
        name="vwidth" 
        select="count(Orte/Ort/Mensch) * 15 + 50"/>
   <xsl:variable 
        name="vheight" 
        select="($vmaxGehalt * $zoomfaktor) 
                + $vAbstandVonOben + $vAbstandVonUnten"/>
   <svg:svg 
        xmlns:svg="http://www.w3.org/2000/svg" 
        width="{$vwidth}" 
        height="{$vheight}" 
        style="fill:gray;">
    <svg:rect x="5" y="5" 
         width="{$vwidth - 5}" 
         height="{$vheight - 5}" 
         style="fill:none; stroke:black"/>
    <svg:text x="50" y="50">Einkommen</svg:text>		 
    <xsl:for-each select="Orte/Ort/Mensch">
     <xsl:sort 
          select="Gehalt" 
          data-type="number" 
          order="ascending"/>
     <svg:text 
          x="{20 + position() *15}" 
          y="{$vheight - $vAbstandVonUnten + 10}" 
          writing-mode="tb">
      <xsl:value-of select="vorname"/>
     </svg:text>
     <svg:rect 
          x="{20 + position() *15}" 
          y="{(($vmaxGehalt - Gehalt) * $zoomfaktor) + $vAbstandVonOben}"
          width="5"
          height="{Gehalt * $zoomfaktor}"
          style="fill:blue"/>
    </xsl:for-each>		
   </svg:svg>
  </xsl:result-document>
</xsl:template>

Auch dieses Ergebnis wurde generiert:

pic/Saeulendiagramm.svg

Nochmals komplexer wird es bei der automatisierten Erstellung von Kreisdiagrammen, weil die Konfiguration der Kreis-Segmente keineswegs trivial ist und zudem auch unterschiedliche Farbgebungen sowie die dazu passende Legende relevant werden. Zum besseren Verständnis macht es Sinn, mit mehreren Variablen zu arbeiten, die z.B. die extern definierten Farbschemata einbinden oder auch mit einer vorsortierten Liste arbeiten.


<xsl:template name="CreateKreisdiagramm">
  <xsl:param name="pwidth">630</xsl:param>
  <xsl:variable name="pheight">450</xsl:variable>
  <xsl:variable name="vcntGesamt">
   <xsl:value-of select="count(/Orte/Ort/Mensch)"/>
  </xsl:variable>
  <xsl:variable name="farbliste_svg" 
       select="document('../input/Farbliste.xml')"/>
  <xsl:variable name="sortierteListe">
   <SORTIERTELISTE>
    <xsl:for-each select="/Orte/Ort/Mensch">
     <xsl:sort select="Gehalt" 
          data-type="number" 
          order="descending"/>
     <Mensch>
      <vorname>
       <xsl:value-of select="vorname"/>
      </vorname>
      <name>
       <xsl:value-of select="name"/>
      </name>
      <Gehalt>
       <xsl:value-of select="Gehalt"/>
      </Gehalt>
     </Mensch>
    </xsl:for-each>
   </SORTIERTELISTE>
  </xsl:variable>
  <xsl:variable name="vGesamtgehalt" 
       select="sum($sortierteListe/SORTIERTELISTE/Mensch/Gehalt)"/>
  <xsl:result-document 
       href="../documentation/Kreisdiagramm.svg" 
       encoding="ISO-8859-1"
       indent="yes">
   <svg width="{$pwidth}" 
       height="{$pheight}" 
       xmlns="http://www.w3.org/2000/svg" 
       version="1.1"
       baseProfile="tiny" 
       xml:lang="de" 
       fill="none">
    <rect x="5" 
          y="5" 
          width="{$pwidth - 5}" 
          height="{$pheight - 5}"
          style="fill:none; stroke:black;"/>
    <title>Einkommen pro Person</title>
    <svg baseProfile="tiny" 
         viewBox="-35 -35 70 70" 
         width="{$pheight - 100}"
         height="{$pheight - 100}" 
         y="25" 
         x="25">
     <circle r="32.831" 
             fill="{$farbliste_svg/FARBEN/farbe[1]/@hex}"
             stroke="black" 
             title="{$sortierteListe/SORTIERTELISTE/Mensch[1]/name}"/>
     <xsl:for-each 
      select="$sortierteListe/SORTIERTELISTE/Mensch">
      <xsl:variable name="pos" select="position()"/>
      <xsl:if test="$pos &amp;gt; 1">
       <xsl:variable name="vbisher">
        <xsl:value-of select="sum(preceding-sibling::*/Gehalt)"/>
       </xsl:variable>
       <xsl:variable name="vAnteil">
        <xsl:value-of select="$vbisher * 100 div $vGesamtgehalt"/>
       </xsl:variable>
       <xsl:variable name="vpendant">
        <xsl:value-of select="100 - $vAnteil"/>
       </xsl:variable>
       <circle r="15.9155" stroke-width="31.831"
        stroke-dasharray="0, {$vAnteil}, {$vpendant}, 0"
        title="{concat(vorname, ' ', name)}">
        <xsl:attribute name="stroke">
         <xsl:value-of 
              select="$farbliste_svg//FARBEN/farbe[$pos]/@hex"/>
        </xsl:attribute>
       </circle>
      </xsl:if>
     </xsl:for-each>
     <circle r="15.3" fill="black" 
      title="Innenkreis Rand"/>
     <circle r="15" fill="white" 
      title="Innenkreis"/>
     <line x2="32" stroke-linecap="round" 
       stroke-width="1" stroke="red" 
       transform="rotate(0)"/>
    </svg>
    <xsl:for-each select="$sortierteListe/SORTIERTELISTE/Mensch">
     <xsl:variable name="pos" select="position()"/>
     <rect x="400" y="{($pos * 20)}" 
      width="10" 
      height="10"
      style="fill:{$farbliste_svg//FARBEN/farbe[$pos]/@hex}; 
             stroke:{$farbliste_svg//FARBEN/farbe[$pos]/@hex};"
      title="{name}"/>
     <text x="430" 
           y="{$pos * 20 + 10}" 
           font-family="Verdana,sans-serif" 
           font-size="9px"
           fill="black">
      <xsl:value-of select="vorname"/>
      <xsl:text> </xsl:text>
      <xsl:value-of select="name"/>
      <xsl:text>: </xsl:text>
      <xsl:value-of select="format-number(Gehalt, '0')"/>
      <xsl:text> Euro (</xsl:text>
      <xsl:value-of 
           select="format-number(Gehalt * 100 div $vGesamtgehalt, '0')"/>
      <xsl:text> %)</xsl:text>
     </text>
    </xsl:for-each>
    <text x="50" 
          y="{$pheight - 20}" 
          font-family="Verdana,sans-serif" font-size="19px"
          font-weight="bold" fill="black">Einkommen pro Person</text>
   </svg>
  </xsl:result-document>
</xsl:template>

Auch dieses Ergebnis kann bewundert werden:

pic/Kreisdiagramm.svg

wg / 9. Februar 2018



Fragen? Anmerkungen? Tips?

Bitte nehmen Sie Kontakt zu mir auf:

Vorname
Nachname
Mailadresse







Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/XSL_SVG.html