Freitag, 25. Mai 2012


Artikel

April 2003 | Artikel

Zimmer frei?

(Link zum Artikel: http://www.entwickler.de/dotnet//000332)

Dynamisch dynamische SVG Vektorgrafiken erstellen

Text: von Alexander Roock
  • Teilen
  • kommentieren
  • empfehlen
  • Bookmark and Share
SVG Vektorgrafiken sind - als eine XML-Sprache - geradezu prädestiniert, über XSL-Transformationen aus XML-Quellen dynamisch erzeugt zu werden. Dieser Artikel beschreibt am Beispiel eines Raumbelegungsplans eine mögliche Vorgehensweise zur Erstellung einer interaktiven SVG-Anwendung mit datenbankgestützten Inhalten auf Basis der Microsoft-Technologien SQL Server 2000, Microsoft XML-Parser/XSLT-Prozessor und .NET.

Die Aufgabe war klar umrissen: Es sollte eine Visualisierung erstellt werden, die die aktuelle Belegung der Seminarräume im Intranet verfügbar macht. Die Informationen, die darzustellen waren, waren ziemlich umfangreich, so sollte zu jedem Raum die Information über Seminartitel, Dozent und alle Teilnehmer verfügbar gemacht werden. Aufgrund der Informationsmenge war es von vornherein klar, dass nicht alle Details auf einmal präsentiert werden konnten. Vielmehr war gewünscht, eine gewisse Interaktivität einzubauen, die es erlaubte, Seminarräume auszuwählen und somit die gleichzeitig dargestellten Informationen überschaubar zu halten.

Zur Lösung der Fragestellung bot sich für uns SVG aus vielerlei Hinsicht an: SVG unterstützt die Erstellung interaktiver Grafiken mittels eingebetteter ECMA-Scripts. Des Weiteren lässt es sich aufgrund der Tatsache, dass es sich um XML handelt, hervorragend mittels XSLT mit weiteren Daten anreichern. Der Nachteil, dass SVG zurzeit noch nicht nativ durch die gängigen Browser unterstützt wird, hat sich für uns nicht gestellt, da wir im Intranet das Adobe SVG Viewer Plugin problemlos verteilen konnten.
Die Lösungsarchitektur
Grob gesehen ergibt sich damit die in Abpictureung 1 veranschaulichte Lösungsarchitektur. Im Mittelpunkt steht eine XSL-Transformation, die durch eine .NET-Applikation initiiert und mittels des Windows-Taskplaners (ermöglicht das zeitgesteuerte Ausführen beliebiger Anwendungen) täglich in den frühen Morgenstunden ausgeführt wird. Das Template (raumbelegung2svg.xslt, siehe Begleit-CD) beschreibt dabei die Art und Weise, wie die XML-Daten vom Datenbankserver in eine SVG-Vektorgrafik übersetzt werden. Um die Belegungsinformationen aus der Datenbank nicht über einen Zwischenschritt selbst nach XML übersetzen zu müssen, nutzen wir die XML-Fähigkeiten des Microsoft SQL Servers 2000. Die .NET-Applikation regt dann lediglich noch den XSLT-Prozessor an, den XML-Datenbankstrom abzufragen, mit Hilfe von XSLT zu transformieren und das Ergebnis in einem Verzeichnis auf dem Webserver bereitzustellen.
Um diese Lösungsarchitektur umzusetzen, sind im Wesentlichen fünf Schritte nötig, die im Folgenden beschrieben werden.
Erstellen der SVG-Vorlage
Zunächst benötigen wir eine SVG-Vorlage, die einen Plan unserer Schulungsräume darstellt. Da uns ein solcher Raumplan bereits als CAD-Datei vorlag, ergaben sich mehrere Möglichkeiten, um zur gewünschten SVG zu kommen. Wir entschlossen uns für die wahrscheinlich am wenigsten Nahe liegende Variante: Wir programmierten die SVG von Hand in einem einfachen Editor. Verrückt?

Nun, die beiden anderen Varianten hatten für uns entscheidende Nachteile: Die einfache Konvertierung (Import der CAD-Datei, Export als SVG) liefert zwar eine schön anzusehende Grafik, die einzelnen Räume sind jedoch nicht als geschlossene Objekte (Rechtecke oder Polygonzüge) in der Grafik vorhanden, was für uns das spätere Umfärben auf einen Mausklick des Benutzers hin schwierig gestaltet hätte. Die zweite Variante wäre das komplette Neuzeichnen des Raumplans z.B. mit Adobe Illustrator oder Corel Draw gewesen, die in ihren aktuellen Versionen SVG exportieren können. Wir entschieden uns trotzdem für die Editor-Variante, weil wir so zum einen einen sehr viel saubereren SVG-Code erzeugen konnten und zum anderen der Aufwand relativ gering war, da wir aus der CAD-Vorlage (bzw. einem GIF-Export) die entscheidenden Koordinaten einfach ablesen und in den SVG-Code übernehmen konnten. Einen Ausschnitt, der die Definition einiger Seminarräume darstellt, sehen sie in Listing 1, das fertige Ergebnis finden sie in der Datei raumplan_basis.svg auf der CD.

Listing 1
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <svg xmlns="http://www.w3.org/2000/svg">
  3. .
  4. .
  5. <!-- Seminarräume -->
  6. <g id="seminarraeume" style="stroke:black;fill:red;fill-opacity:0.6;stroke-width:1px;">
  7. <path id="r4" d="M 666,129 L 695,98 L 657,67 L 630,98 Z"/>
  8. <path id="r5" d="M 630,98 L 657,67 L 627,54 L 619,88 Z"/>
  9. <path id="r6" d="M 619,88 L 627,54 L 623,51 L 568,49 L 568,88 Z"/>
  10. <rect id="r7" y="49" x="505" width="63" height="39"/>
  11. <rect id="r8" y="49" x="459" width="46" height="39"/>
  12. .
  13. .
  14. </g>
  15. .
  16. .
  17. </svg>
Erweitern der SVG-Vorlage um Interaktivität
Die erste Hürde war genommen, wir wollten jedoch eine Grafik, auf der der Benutzer die einzelnen Räume auswählen, diese andersfarbig dargestellt und die Raumbelegungsinformationen angezeigt werden konnten. Dazu haben wir die Vorlage um ECMA-Script-Anteile erweitert, die Benutzermausklicks auswerten und die beschriebenen Änderungen vornehmen.

Das Scripting in SVGs ähnelt sehr stark dem in HTML. Kein Wunder, schließlich ist ECMA-Script die standardisierte Variante von JavaScript. So ist die Vorgehensweise dann auch praktisch identisch. Zunächst muss ein Event Handler registriert werden, welcher auf den Objekten definiert wird, die auf das gewünschte Event reagieren sollen. Beispielhaft folgt hier die notwendige Änderung für einen Raum:
  1. <rect onactivate="splitter(7)" id="r7" y="49" x="505" width="63" height="39"/>
Des Weiteren fügen wir in das SVG für jeden Seminarraum noch einen Textbereich ein, der zunächst nur den Raumnamen anzeigt, später jedoch um die weiteren Informationen aus der Datenbank ergänzt wird. Dieser Textbereich ist standardmäßig ausgeblendet (visibility-Eigenschaft auf hidden gesetzt) und wird bei Aktivierung des jeweiligen Raumes eingeblendet:
  1. <text visibility="hidden" id="t7" x="30" y="190">
  2. <tspan font-size="120%" x="30" dy="2.5ex">Raum 7 / Friedrichshain</tspan>
  3. </text>
Der onactivate-Event Handler wird nun also aufgerufen, wenn das betreffende Objekt aktiviert, also z.B. per Maus angeklickt wird. Aufgerufen wird eine Funktion namens splitter, der die Raumnummer des ausgewählten Raums als Parameter mitgegeben wird. Die Funktion und den Rest des Skripts finden Sie in Listing 2.

Listing 2
  1. <svg xmlns="http://www.w3.org/2000/svg">
  2. <script type="text/ecmascript"><![CDATA[
  3. var state = new Array();
  4. for ( var i = 0 ; i <= 35 ; i++ ){
  5. state[i] = false;
  6. }
  7. function splitter(no)
  8. {
  9. if ( state[no] == false )
  10. turnOn(no);
  11. else
  12. turnOff(no);
  13. }
  14. function turnOn(no){
  15. turnAllOff();
  16. var target = document.getElementById("r"+no);
  17. target.getStyle().setProperty("fill-opacity","1");
  18. target = document.getElementById("t"+no);
  19. target.getStyle().setProperty("visibility","show");
  20. state[no] = true;
  21. }
  22. function turnOff(no){
  23. var target = document.getElementById("r"+no);
  24. target.getStyle().setProperty("fill-opacity","0.6");
  25. target = document.getElementById("t"+no);
  26. target.getStyle().setProperty("visibility","hidden");
  27. state[no] = false;
  28. }
  29. function turnAllOff(){
  30. var target;
  31. for ( var i = 0 ; i <= 35 ; i++ ){
  32. if ( state[i] == true ){
  33. target = document.getElementById("r"+i);
  34. target.getStyle().setProperty("fill-opacity","0.6");
  35. target = document.getElementById("t"+i);
  36. target.getStyle().setProperty("visibility","hidden");
  37. state[i] = false;
  38. }
  39. }
  40. }]]></script>
  41. .
  42. .
  43. .
Die durch den Event Handler aufgerufene Funktion splitter überprüft im eigens dafür angelegten Feld state[] den Zustand des Raumes (ein- oder ausgeschaltet) und verzweigt entsprechend in die Funktion turnOn() oder turnOff(). Die Funktion turnOn() vollführt folgende vier Schritte:
  • Deaktivieren aller anderen Räume und ausblenden aller Textbereiche (turnAllOff())
  • Aktivieren des zur Raumnummer passenden Raums durch Intensivierung der Füllfarbe
  • Einblenden des zum Raum gehörenden Textbereichs
  • Status des Raumes im state[]-Feld auf true setzen
Für die Punkte 2 und 3 muss sich das Skript die entsprechenden Objekte des DOMs (Document Object Models) holen. Um diesen Vorgang zu vereinfachen, tragen sowohl der Raum selbst, als auch der zum Raum gehörende Textbereich eine eindeutige ID, die sich aus Raumnummer und einem Präfix zusammensetzt.

Die beiden Funktionen turnOff() und turnAllOff() funktionieren prinzipiell analog, deaktivieren jedoch Raum und zugehörigen Text. Das komplette, interaktive SVG finden sie in der Datei raumplan_interaktiv.svg.

Das so entstandene SVG wird uns als Vorlage für das XSLT-Stylesheet dienen, welches den XML-Datenbankstrom in eben jenes SVG transformiert. Bevor wir uns aber mit dem Stylesheet beschäftigen, wollen wir einen Blick auf die Datenbank werfen.
SQL Server 2000 und XML
Sicherlich kann man auch den traditionellen Weg gehen und das Resultset der Datenbankabfrage selbst in das gewünschte XML-Format wandeln. Ein komfortablerer Weg ist es, die XML-Fähigkeit aktueller Datenbanksysteme wie MS SQL Server oder Oracle 9i zu nutzen.

Microsofts SQL Server z.B. bringt seit der Version 2000 eine native Unterstützung für XML mit, die es uns unter anderem erlaubt, Ergebnisse auf SQL-Anfragen als XML zu erhalten. Dazu kann jedes SQL-Statement um die Schlüsselwörter FOR XML ergänzt werden, wobei durch verschiedene Modi die Art und Weise der XML-Generierung beeinflusst werden kann. Durch das Zusammenspiel mit dem Microsoft Webserver IIS wird es sogar möglich, Datenbankabfragen über HTTP an den SQL Server zu richten und den XML-Ergebnisstrom so durch Firewalls zu schleusen.

Eine genaue Beschreibung der XML-Fähigkeiten des SQL Servers würde den Rahmen dieses Artikels sprengen, hier sei nur so viel gesagt: In unserem Szenario wird der SQL Server über den Microsoft IIS per HTTP abgefragt, wobei ein Datumswert als URL-Parameter mitgegeben wird. Die SQL-IIS-Integration sorgt für den Aufruf einer parametrisierten Stored Procedure auf dem SQL Server, welcher das Datum mitgegeben wird. Das XML-Ergebnis der Datenbank wird noch mittels einer XSL-Transformation (welche hier nicht näher beschrieben wird) in ein für uns besser zu verarbeitendes XML-Format gewandelt, bevor der IIS es an uns weiterreicht (siehe auch Abb. 2).
In den Beispieldateien auf der Heft-CD finden sie zum Nachvollziehen sowohl das Schema, das die vom IIS gelieferte XML-Struktur beschreibt (raumbelegung.xsd), als auch ein Beispieldokument für eine konkrete XML-Instanz (raumbelegung.xml).
SQLXML für SQL Server 2000
Die Fähigkeiten des SQL Server 2000 bezüglich XML sind bestimmt durch den technologischen Stand zum Erscheinungstermin des SQL Servers. Mittlerweile haben sich einige XML-Standards entscheidend geändert, sodass Microsoft Anpassungen und auch Erweiterungen an die veränderten Standards als zusätzliches Paket für den SQL Server zur Verfügung stellt. SQLXML in der aktuellen Version 3.0 SP1 finden sie unter folgendem Link: msdn.microsoft.com/downloads/default.asp?url=/downloads/sample.asp?url=/msdn-files/027/001/824/msdncompositedoc.xml
Das SVG Transformations Stylesheet
Aufgabe des Stylesheets ist die Transformation des eben beschriebenen XML-Stroms in das beschriebene interaktive SVG. Listing 3 zeigt einen Ausschnitt aus dem Stylesheet, das komplette Stylesheet finden sie als raumbelegung2svg.xslt auf der CD.

Im Stylesheet sorgt das erste Template für die Erzeugung des Grundgerüsts der SVG, wie wir es in raumplan_interaktiv.svg definiert haben. Dabei werden im Wesentlichen zwei Informationen ergänzt: Zunächst wird jedem Raum (hier gezeigt für den Raum mit der Nummer 7) ein Attribut hinzugefügt, welches die Raumfarbe auf Grün stellt, falls in der Datenbankabfrage keine Informationen zu diesem Raum enthalten sind. Diese Räume sind also frei, alle anderen Räume werden mit der Standardfüllfarbe Rot gefüllt.

Die zweite Änderung bezieht sich auf die Belegungsinformationen zu den Räumen. Zu jedem Raum existiert ein ausgeblendeter Textkasten, wobei die XSLT-Anweisung überprüft, ob es Raumbelegungsdaten für diesen Raum im XML-Dokument gibt und gegebenenfalls das zweite Template aufruft, welches dann aus dem XML-Strom Informationen wie Dozent und Teilnehmernamen in das SVG übernimmt.

Eine wichtige Rolle nimmt noch die output-Anweisung (2. Zeile) ein, in der wir die doctype-Deklaration für das Zieldokument vornehmen und das script-Tag als CDATA-Abschnitt des Zieldokuments auszeichnen.

Listing 3
  1. <xsl:stylesheet xmlns:svg="http://www.w3.org/2000/svg" exclude-result-prefixes="svg">
  2. <xsl:output doctype-public="-//W3C//DTD SVG 1.0//EN" doctype-system="http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" cdata-section-elements="svg:script" media-type="text/xml"/>
  3. <xsl:template match="/">
  4. <svg xmlns="http://www.w3.org/2000/svg">
  5. .
  6. .
  7. <rect onactivate="splitter(7)" id="r7" y="49" x="505" width="63" height="39">
  8. <xsl:if test="not(raumbelegung/raum[@nummer='07'])"><xsl:attribute
  9. name="fill">green</xsl:attribute></xsl:if>
  10. </rect>
  11. .
  12. .
  13. <text visibility="hidden" id="t7" x="30" y="190">
  14. <tspan font-size="120%" x="30" dy="2.5ex">Raum 7 / Friedrichshain</tspan>
  15. <xsl:apply-templates select="raumbelegung/raum[@nummer='07']"/>
  16. </text>
  17. .
  18. .
  19. </svg>
  20. </xsl:template>
  21. <xsl:template match="raum" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
  22. <tspan font-weight="bold" x="30" dy="5ex">
  23. <xsl:value-of select="seminar"/>
  24. (<xsl:value-of select="seminar/@nr"/>)
  25. </tspan>
  26. <tspan stroke="red" x="30" dy="4ex">
  27. <xsl:value-of select="dozent/vorname"/>
  28. <xsl:value-of select="dozent/name"/>
  29. </tspan>
  30. <tspan>(Dozent)</tspan>
  31. <xsl:for-each select="teilnehmerliste/teilnehmer">
  32. <tspan x="30" dy="2.5ex">
  33. <xsl:value-of select="vorname"/>
  34. <xsl:value-of select="name"/>
  35. </tspan>
  36. </xsl:for-each>
  37. </xsl:template>
  38. </xsl:stylesheet>
Das Ergebnis der Transformation ist der fertige SVG-Raumbelegungsplan, wie in Abpictureung 3 zu sehen. Wir müssen nun lediglich dafür sorgen, dass die Transformation täglich in den frühen Morgenstunden automatisiert durchgeführt wird, damit ein täglich aktualisierter Plan vorliegt.
Steuerung der Transformation
Für die Transformation haben wir eine kleine kommandozeilenbasierte .NET-Applikation geschrieben, die den im .NET Framework enthaltenen XSLT-Prozessor nutzt. Die Applikation ruft über HTTP das XML-Ergebnis der Datenbank ab und wendet darauf das gerade beschriebene Stylesheet an, um das Ergebnis der Transformation im Filesystem abzulegen.

Die .NET-Applikation wird über den Windows-Taskplaner (zu finden in der Systemsteuerung) täglich zu einer vorgegebenen Zeit ausgeführt, was für unser Szenario ausreicht, da sich die Raumbelegung über den Tag hinweg nicht mehr ändert.

Zum einfacheren Nachvollziehen finden sie in den Beispieldateien auf der CD eine .NET Windows-Applikation, mit der sie die Transformation der Beispieldaten durchführen können, die Applikation setzt ein installiertes .NET-Framework auf ihrem Rechner voraus.
Fazit
Dank Technologien wie SVG und der XML-Fähigkeiten moderner Datenbanksysteme kann man mit minimalem Programmieraufwand und unter Zuhilfenahme von XML-Technologien durchgehend - vom Datenspeicher bis zur Visualisierung - XML-basiert entwickeln. Ohne Technologiebruch lassen sich Projekte dieser Art in kürzester Zeit realisieren, so wurde dieses Projekt z.B. inklusive Konzeptionsphase in lediglich zwei Mann-Tagen umgesetzt.
Links und Literatur

Kommentare