(aktualisiert!)

IT-Trainings in Zeiten des Corona-Virus, Veranstaltungstipps der www.gfu.net:
Schulung XML Einfuehrung und Grundlagen *** Schulung Python fuer Programmiereinsteiger *** Schulung XML, XSLT, XPath, XSL-FO Einfuehrung *** Schulung Java/JSP/HTML/JDBC/XML Komplett *** Schulung XML mit Altova XMLSpy *** Schulung XML Grundlagen, XSL-FO, SVG, DocBook, DITA *** Schulung XML - der professionelle Ueberblick *** Schulung XML-Schema Intensiv *** Schulung Einstieg in die Programmierung *** Schulung C++ fuer Programmiereinsteiger *** Schulung VB.NET fuer Visual Basic-Programmierer ***


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, dass 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 erwähnt) die Null-Koordinate der Zeichenfläche sich oben links auf der Zeichenfläche befindet. Dadurch muss 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

Noch mal 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. Diese Farbliste könnte so aussehen:


<FARBEN>
    <farbe hex="#138808" name="India green"/>
    <farbe hex="#177245" name="Dark spring green"/>
    <farbe hex="#059033" name="North Texas Green"/>
    <farbe hex="#03C03C" name="Dark pastel green"/>
    <farbe hex="#CEFF00" name="Volt"/>
    <farbe hex="#FDFD96" name="Pastel yellow"/>
    <farbe hex="#FFFF66" name="Laser Lemon"/>
    <farbe hex="#FFFF33" name="Electric yellow"/>
    <farbe hex="#FEDF00" name="Yellow (Pantone)"/>
    <farbe hex="#FB9902" name="Orange (RYB)"/>
    <farbe hex="#FD0E35" name="Scarlet"/>
    <farbe hex="#FF1493" name="deeppink"/>
    <farbe hex="#FD6C9E" name="French pink"/>
    <farbe hex="#FFC0CB" name="pink"/>
    <farbe hex="#FDD5B1" name="Feldspar"/>
    <farbe hex="#CC99CC" name="Light grayish magenta"/>
    <farbe hex="#CC99FF" name="Pale violet"/>
    <farbe hex="#871F78" name="darkpurple"/>
    <farbe hex="#6F2DA8" name="Grape"/>
    <farbe hex="#6C3082" name="Eminence"/>
    <farbe hex="#66023C" name="Imperial purple"/>
    <farbe hex="#000080" name="navy"/>
    <farbe hex="#FE28A2" name="Persian rose"/>
    <farbe hex="#FDBCB4" name="Melon"/>
    <farbe hex="#6F00FF" name="Electric indigo"/>
    <farbe hex="#6E6EF9" name="Dark imperial blue"/>
    <farbe hex="#87CEFA" name="lightskyblue"/>
    <farbe hex="#66DDAA" name="Medium aquamarine"/>
    <farbe hex="#BCD4E6" name="Beau blue"/>
    <farbe hex="#B0E0E6" name="powderblue"/>
    <farbe hex="#E6E6FA" name="lavendel"/>
    <farbe hex="#CCFF00" name="Electric lime"/>
    <farbe hex="#a6d608" name="Vivid lime green"/>
    <farbe hex="#987654" name="Pale brown"/>
    <farbe hex="#965A3E" name="Coconut"/>
    <farbe hex="#778BA5" name="Shadow blue"/>
    <farbe hex="#74C365" name="Mantis"/>
    <farbe hex="#76FF7A" name="Screaming Green"/>
    <farbe hex="#664C28" name="Donkey brown"/>
    <farbe hex="#663399" name="Rebecca Purple"/>
    <farbe hex="#000000" name="black"/>
</FARBEN>

Deren Einbindung in das XSL-Stylesheet funktioniert so:


<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 / 18. Dezember 2019



Fragen? Anmerkungen? Tipps?

Bitte nehmen Sie Kontakt zu mir auf.






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