Kreis-Sektoren und -Diagramme mit SVG

Document Object Model (DOM)

Zielgruppe sind StudentInnen und ambitionierte Informatik-Amateure

Grundkenntnisse von SVG Objekt-Grafik werden vorausgesetzt, Kenntnisse einer Programmiersprache sind hilfreich.





Zeichnung

Die Standard-'Programmiersprache' für Objekt-Grafik → SVG kennt leider kein KreisSektor Element. Man muss daher ein <path>-Element verwenden, welches - im Gegensatz zu allen anderen SVG-Elementen - weder einfach noch anschaulich zu programmieren ist.

Zur Programmierung eines <path>-Elements muss man die relativen Zahlenwerte in Winkel (ZentralWinkel der Kreis-Sektoren) umrechnen:
Der rote Sektor sollte mit einem ZentralWinkel von 0° gezeichnet werden, der grüne Sektor mit 0°

Die folgenden Kapitel unterstützen die Programmierung von Kreis-Sektoren mit SVG.

Programm-Generator für ein komplettes SVG-Diagramm




yyy

Programm-Generator für 1 SVG <path>-Element




Der 'Kreis'-Quelltext erzeugt ein <circle>-Element:  Wenn man damit den Hintergrund programmiert, dann kann man das letzte <path>-Element einsparen.
Der 'Bogen'-Quelltext erzeugt eine Kreisbogen-Linie ohne Füllung.
Der 'Sektor'-Quelltext erzeugt einen gefülltes Kreis-Sektor, so wie im Beispiel rechts neben den Vorgaben angezeigt.

Dieser Programm-Generator erfordert zur Anwendung Grundkenntnisse von SVG. Einige Details der Anwendung werden in den folgenden Absätzen vorgestellt.


Der Programm-Generator steht auch in einer Version für Tabellen-Kalkulations-Programme zur Verfügung:  Sie können das Archiv ↗ SVG-Kreisdiagramm.zip herunterladen, auspacken und die darin enthaltene Datei SVG-Kreisdiagramm.xls mit jedem Standard Tabellen-Kalkulations-Programm (z.B. mit LibreOffice oder MS-Excel) verwenden.


Formatierung

Die hier verwendeten SVG-Elemente werden nur dann sichtbar angezeigt, wenn man einige Format-Eigenschaften hinzufügt. Das gilt sowohl für <circle>- als auch für <path>-Elemente.

Beispiel:
<circle cx="0" cy="0" r="50" fill="yellow" stroke="blue" stroke-width="2"/>
Das fill-Attribut bestimmt die Farbe der Füllung.
Das stroke-Attribut bestimmt die Farbe der Umrahmung.
Das stroke-width-Attribut bestimmt die Breite der Umrahmung.

Alternativ kann man diese Attribute mit → CSS style-Attributen oder besser in einem CSS <style>-Element angeben, z.B.
<circle cx="0" cy="0" r="50" style="fill:yellow; stroke:blue; stroke-width:2px;"/>



So sieht der SVG-Quelltext für ein Kreis-Diagramm der 3 links berechneten Werte aus:
<svg xmlns="http://www.w3.org/2000/svg">
<style>
path{stroke:black; stroke-width:2px;}
#p1{fill:red;}
#p2{fill:blue;}
#p3{fill:yellow;}
</style>
<g transform="translate(50,50)">
<path id="p1" d="M0,0 L0.0,-50.0 A50.0,50.0 0 0 1 43.3,-25.0 Z" />
<path id="p2" transform="rotate(60)" d="M0,0 L0.0,-50.0 A50.0,50.0 0 0 1 43.3,25.0 Z" />
<path id="p3" transform="rotate(180)" d="M0,0 L0.0,-50.0 A50.0,50.0 0 0 1 0.0,50.0 Z" />
</g>
</svg>
Im <style>-Element wird die → CSS-Formatierung aller Elemente festgelegt.
Alle Elemente des Kreis-Diagramms werden in ein <g>-Element eingeschlossen.
Mit dem transform-Attribut des <g>-Elements wird das gesamte Diagramm nach rechts unten verschoben.
Innerhalb der <g> Gruppe werden die 3 mit dem Generator berechneten <path>-Elemente angeordnet.


Man kann die SVG-Datei in jedes Teil-Programm von LibreOffice (z.B. Textverarbeitung, Präsentation) einsetzen, oder man kann die Grafik im LibreOffice Zeichen-Programm (Draw) ergänzen oder verändern.

Man kann die SVG-Datei zur weiteren Verarbeitung auch in das kostenfreie ObjektGrafik-Programm InkScape oder in das kostenfreie PixelGrafik-Programm → GIMP importieren. Beide Programme bieten auch die Umwandlung in andere (Objekt- und PixelGrafik) → Datei-Formate.

Live-Berechnung


Die vorgestellte Funktion ist zwar portabel, die Anwendung in eigenen Programmen erfordert jedoch fortgeschrittene Kenntnisse von Javascript und mindestens Grundkenntnisse von → SVG und → CSS.
Mit entsprechenden Kenntnissen kann man die Funktion in jede andere Programmiersprache portieren.

Die hier vorgestellte Javascript-Funktion calc_arc() wird in den Live-Beispielen dieser Seite (in leicht modifizierter Form) verwendet.


Javascript-Quelltext

Die Funktion erwartet diese 5 Argumente:
  • r...Radius des Diagramm-Kreises (in Pixeln)
  • a_deg...Start-Winkel in Grad (0...360)
  • z_deg...End-Winkel in Grad (a_deg...720)
  • ne...Winkel-System ('N'orth oder 'E'ast)
  • opt...Element-Typ ('A'rc oder 'S'ector)
Die Funktion gibt eine Objekt-Variable mit 2 Elementen zurück:
  • d...Wert des d-Attributs eines <path>-Elements als String
  • r...Wert des Rotations-Winkels in Grad als Zahl

Zuerst werden die Argumente validiert:
Der Radius muss r>=0 sein.
Das Winkel-System 'N' beginnt oben und zählt Winkel im Sinne des Uhrzeigers. Das Winkel-System 'E' beginnt rechts und zählt Winkel entgegen der Richtung des Uhrzeigers.
Die beiden vorgegebenen Winkel a_deg und z_deg werden kontrolliert und bei Bedarf angepasst. Wenn a_deg>0 dann werden beide Winkel um diesen Wert reduziert und später wird ein transform="rotate()"-Attribut erzeugt.
Mit Option 'A' erzeugt die Funktion einen Kreisbogen ohne Füllung, mit Option 'S' ein Kreis-Segment mit Füllung. Die Formatierung mit CSS muss man allerdings selbst ergänzen.

Im nächsten Schritt werden die Vorgabe-Winkel a_deg,z_deg in das Bogenmaß a_rad,z_rad umgerechnet.

Nun wird schrittweise der Code des d-Attributs in der Variablen d erzeugt. Dazu gibt es mehrere Möglichkeiten. Hier werden Anweisungen mit absoluten Koordinaten verwendet: 'M' bedeutet MoveTo, 'L' bedeutet LineTo, 'A' bedeutet ArcTo
Die Anweisung 'A' erwartet 7 Zahlenwerte als Argumente:
  • rx...Radius in X-Richtung
  • ry...Radius in Y-Richtung
  • frot...Rotation-Flag (hier =0)
  • fla...LargeArc-Flag: Ist =0 wenn der ZentralWinkel <180°, andernfalls =1
  • fs...Sweep-Flag (hier =1)
  • xz...X-Koordinate des Endpunkts
  • yz...Y-Koordinate des Endpunkts
Die Anweisung 'Z' schließt den Pfad (nur für ein Kreis-Segment).

Wenn der Start-Winkel a_deg>0 ist, dann wird der Wert eines transform-Attributs erzeugt, andernfalls wird diese Variable =null gesetzt.

Zuletzt werden die berechneten Werte als Objekt mit 2 Eigenschaften zurückgegeben. Die Eigenschaft d enthält den Text des d-Attributs, die Eigenschaft r den Rotations-Winkel als Zahl.



Die programmierte Änderung der Diagramm-Segmente erfolgt mit → DOM-Methoden.
Die <path>-Elemente werden mit ihren id-Attributen adressiert. Das funktioniert jedoch erst dann, wenn bereits alle SVG-Elemente geladen wurden. Daher werden die Javascript-Funktionen erst nach dem onload-Ereignis ausgeführt, z.B.
<svg onload="do_onload()">...</svg>

Voraussetzung ist die Berechnung der Anfangs- und End-Winkel aller gewünschten Kreis-Segmente in Grad.
Mit Funktion calc_arc() werden die Werte der beiden Attribute berechnet, z.B.:
obj=calc_arc(50,0,60,'N','S');
Jedes <path>-Element wird mit seinem id-Attribut adressiert. Anschließend werden die beiden Attribute angegeben, z.B.:
p=document.getElementById('p1');
p.setAttribute('d',obj.d);
t='rotate('+obj.r+')';
p.setAttribute('transform',t);


Eine allgemeine Lösung ist eleganter:  In diesem Fall enthält der SVG-Quelltext keine <path>-Elemente. Das umschließende <g>-Element erhält ein id-Attribut, z.B.:
<g id="pie" transform="translate(...)"></g>
Alle gewünschten <path>-Elemente werden mit ihren Attributen Live erzeugt und danach an das <g>-Element angehängt, z.B.:
g=document.getElementById('pie');
ns='http://www.w3.org/2000/svg';
p=document.createElementNS(ns,'path');
p.setAttribute('id','p1');
p.setAttribute('d',obj.d);
t='rotate('+obj.r+')';
p.setAttribute('transform',t);
g.appendChild(p);
Wenn ein bereits erzeugtes Diagramm geändert werden soll, dann muss man vorher alle vorhandenen <path>-Elemente löschen:
g=document.getElementById('pie');
while(g.childNodes.length>0) {
g.removeChild(g.firstChild);
}

Syntax des  <path>-Elements


In diesem Kapitel werden einige Aspekte der Programmierung an Hand von typischen Beispielen vorgestellt. Damit werden SVG-Referenzen bestenfalls ergänzt, jedoch nicht ersetrzt.



Alle 4 Pfade verwenden diese gleichen Vorgaben:
Mittelpunkt (0,0), Kreis-Radius r=50 (Pixel) und KreisSektor- Winkel w=90° Die 4 Pfade unterscheiden sich in den beiden jeweils angegebenen Auswahl-Schaltern LargeArcFlag und SweepFlag



In Kreis-Diagrammen mit dem Mittelpunkt (0,0) und dem Radius r vereinfacht sich die Syntax des d-Attributs:

Die LineTo-Anweisung (vom Ursprung zum grünen Punkt) hat immer die Form
L0,r
(Im Quelltext muss man den Zahlenwert von r einsetzen)

Die ArcTo-Anweisung (vom grünen zum roten Punkt) hat immer die Form
Ar,r 0 0,1 x,y
Die Koordinaten x,y des rot bezeichneten Kreisbogen-Endpunkts werden aus dem Radius und dem Zentralwinkel w_rad des Sektors (im Bogenmaß) berechnet:
x = r * sin(w_rad)
y = r * cos(w_rad)



yyy



(3) Koordinaten des Endpunkts
x = xm + r * sin(w_rad)
y = ym - r * cos(w_rad)
Beispiel für w_deg=60°, r=50, xm=0; ym=0
x = 0 + 50 * sin(1.047) = 43.30
y = 0 - 50 * cos(1.047) = -25.00


(4) Ergebnis
<path d="M0,0 v-50 A50,50 0 0,1 43.3,-25 Z" />

(5) Drehung Für ein Kreis-Diagramm (pie-chart) müssen die Segmente meistens noch um den Mittelpunkt gedreht werden.
Beispiel: Drehung des Segments um 30°
<path d="M0,0 v-50 A50,50 0 0,1 43.3,-25 Z" transform="rotate(30)" />




Diese SVG-Anweisung erzeugt das gleiche Ergebnis weniger anschaulich, jedoch mit weniger Aufwand:
<path d="M0,0 L25,-43.3 A50,50 0 0,1 50,0 Z" />
Der Anfang des Kreisbogens wird bereits um 30° gedreht (L25,-43.3), das Ende liegt 60° weiter am Punkt 50,0.