An sich besteht eine Horoskopzeichnung aus wenigen graphischen Elementen: Einige Linien zur Abgrenzung der Zeichen, weitere Markierungsstriche für die einzelnen Grade, dann die Tierkreiszeichen, die Häuserlinien und die Planetensymbole. Idealerweise wählt man für solche Graphiken ein sogenanntes Vektorformat. Das speichert die Abbildung nicht pixelweise, sondern in Form von sogenannten geometrischen Primitiven wie "Ziehe eine dünne blaue Linie von Punkt 1 nach Punkt 2" oder: "Schlage einen Kreis um Punkt 3 mit Radius R und fülle ihn gelb" und dergleichen. Das garantiert die Skalierbarkeit der Graphik: Wenn man der Graphik einen grösseren Platz zur Darstellung gibt, ist sie in der Lage, diesen Platz auch besser auszunutzen und das Bild in grösserer Schärfe darzustellen. Im Gegensatz dazu wird eine Pixel- oder Rastergraphik bei Vergrösserung immer ungenauer. Kurven werden abgehackt und unregelmässig, man beginnt den Bildaufbau aus den Pixeln zu erkennen. Zu den klassischen Rastergraphikformaten gehören PNG, GIF, JPG und BMP.
Vektorgraphiken können mit dem sehr schönen, aber leider nicht so gebräuchlichen "Encapsulated PostScript" Format (eps) dargestellt werden. Erwähnenswert ist auch die "Vector Markup Language", eine Erweiterung von HTML um Vektorgraphik-Primitive, leider aber ein proprietäres Produkt von Microsoft, womit man sich um die Browserunabhängigkeit begeben würde. Auch pdf-Graphiken sind Vektorgraphiken — nur wäre es unschön, das pdf-Format in Webseiten zur Anzeige von eingebetteten Horoskopgraphiken zu verwenden. pdf ist gut, wenn auch das umgebende Dokument pdf ist. Zur Anzeige Bildern, die in eine HTML-Seite eingebettet sind, eignet es sich weniger.
Das SVG-Format ist XML-basiert. Eine SVG-Graphik ist in der Tat nichts anderes als ein XML-Dokument, Instanz eines fest definierten XML-Schemas. Einzelne Elemente des Dokuments entsprechen bestimmten graphischen Anweisungen. Beispielsweise bedeutet
<path d="M50 50 L 100 100"/>eine Linie von dem Punkt (50,50) zum Punkt (100,100). Ein anderes Beispiel: Das Element
<circle cx="100" cy="100" r="50"/>steht für einen Kreis um den Punkt (100,100) mit dem Radius 50.
Diese Elemente lassen sich nun mit CSS-Angaben formatieren und über die DOM-API-Funktionen mit Scriptsprachen ansprechen, wie man das von HTML kennt. Beispielsweise würde man durch die Stylesheetangabe
circle { fill:red; }
festlegen, dass alle in der Graphik auftretenden Kreise mit roter Farbe zu füllen sind. Alle Bestandteile der Graphik lassen sich dynamisch mit JavaScript-Mitteln ändern. Beispielsweise setzt man mit
document.getElementById("graphik1").getElementsByTagName("circle")[0].r = "100";
den Radius des ersten in der Graphik mit der ID graphik1 auftretenden Kreises auf 100.
Wenn Sie in einer HTML-Seite auf das folgende XML-Dokument als SVG-Graphik verweisen (in einer Form, die Sie gleich noch kennenlernen werden) und Ihre Seite in einem Browser öffnen, wird idealerweise die zu diesen Daten gehörende Horoskopgraphik angezeigt – der Browser wird beim Laden der Graphik durch die Anweisung <?xml-stylesheet...?> dazu veranlasst, das Programm horo.xsl auf die im Dokument enthaltenen Planeten- und Hauspositionen anzuwenden.
<?xml version="1.0" encoding="iso-8859-1"?>
<?xml-stylesheet href="http://www.astrotexte.ch/sources/horo.xsl" type="text/xsl"?>
<horoscope>
<planets>
<so>335.3</so>
<mo>52.2</mo>
<me>313.5</me>
<ve>10.7</ve>
<ma>329.7</ma>
<ju>18.2</ju>
<sa>325.15</sa>
<ur>158.3</ur>
<ne>227.9</ne>
<pl>163.25</pl>
</planets>
<houses>
<asc>211</asc>
<h2>238</h2>
<h3>273</h3>
<ic>312</ic>
<h5>346</h5>
<h6>11.5</h6>
</houses>
</horoscope>
Bevor ich den Mechanismus auf dieser Webseite vorführe, wollen wir uns die Transformation horo.xsl noch etwas genauer ansehen.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:g="http://www.w3.org/2000/svg"
xmlns:xl="http://www.w3.org/1999/xlink">
<xsl:output method="xml" version="1.0" encoding="iso-8859-1"
indent="yes"
cdata-section-elements="g:script"
doctype-public="-//W3C//DTD SVG 1.0//EN"
doctype-system="http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"/>
<xsl:template match="horoscope">
<xsl:variable name="imgRoot">http://www.astrotexte.ch/graphics</xsl:variable>
<xsl:processing-instruction name="xml-stylesheet">href="horo.css" type="text/css"</xsl:processing-instruction>
<g:svg xmlns:g="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" width="400" height="400" onload="doOnLoad(evt);">
<g:script type="text/javascript" xl:href="horo.js"/>
<g:script type="text/javascript">
function doOnLoad(iEvent) {
var lHoro =
new HoroSVG(
[0<xsl:for-each select="./planets/*">,<xsl:value-of select="."/></xsl:for-each>],
[0<xsl:for-each select="./houses/*">,<xsl:value-of select="."/></xsl:for-each>] );
lHoro.draw();
}
</g:script>
<g:g class="line">
<g:circle id="outer" cx="200" cy="200" r="180" />
<g:circle id="inner" cx="200" cy="200" r="150"/>
<g:circle id="center" cx="200" cy="200" r="50"/>
<g:line id="h1"/>
<g:line id="h2"/>
<g:line id="h3"/>
<g:line id="h4"/>
<g:line id="h5"/>
<g:line id="h6"/>
<g:path id="tickMarks"/>
<g:path id="signMarks"/><!-- Zeichen: Widder = ♈, etc. -->
<g:image id="planet1" width="13" height="15" xl:href="{$imgRoot}/SO.gif"/>
<g:image id="planet2" width="13" height="15" xl:href="{$imgRoot}/MO.gif"/>
<g:image id="planet3" width="12" height="18" xl:href="{$imgRoot}/ME.gif"/>
<g:image id="planet4" width="13" height="18" xl:href="{$imgRoot}/VE.gif"/>
<g:image id="planet5" width="15" height="16" xl:href="{$imgRoot}/MA.gif"/>
<g:image id="planet6" width="14" height="15" xl:href="{$imgRoot}/JU.gif"/>
<g:image id="planet7" width="11" height="14" xl:href="{$imgRoot}/SA.gif"/>
<g:image id="planet8" width="12" height="17" xl:href="{$imgRoot}/UR.gif"/>
<g:image id="planet9" width="14" height="16" xl:href="{$imgRoot}/NE.gif"/>
<g:image id="planet10" width="15" height="14" xl:href="{$imgRoot}/PL.gif"/>
<g:image id="sign1" width="11" height="13" xl:href="{$imgRoot}/AR.gif"/>
<g:image id="sign2" width="11" height="13" xl:href="{$imgRoot}/TA.gif"/>
<g:image id="sign3" width="11" height="13" xl:href="{$imgRoot}/GE.gif"/>
<g:image id="sign4" width="11" height="13" xl:href="{$imgRoot}/CN.gif"/>
<g:image id="sign5" width="11" height="13" xl:href="{$imgRoot}/LE.gif"/>
<g:image id="sign6" width="11" height="13" xl:href="{$imgRoot}/VI.gif"/>
<g:image id="sign7" width="11" height="13" xl:href="{$imgRoot}/LI.gif"/>
<g:image id="sign8" width="11" height="13" xl:href="{$imgRoot}/SC.gif"/>
<g:image id="sign9" width="11" height="13" xl:href="{$imgRoot}/SG.gif"/>
<g:image id="sign10" width="11" height="13" xl:href="{$imgRoot}/CP.gif"/>
<g:image id="sign11" width="11" height="13" xl:href="{$imgRoot}/AQ.gif"/>
<g:image id="sign12" width="11" height="13" xl:href="{$imgRoot}/PS.gif"/>
</g:g>
</g:svg>
</xsl:template>
</xsl:stylesheet>
Ein paar Bemerkungen zu diesem XSLT-Programm sind angebracht:
lHoro.draw()im onload-Behandler der Graphik.
new HoroSVG([0<xsl:for-each select="./planets/*">,<xsl:value-of select="."/></xsl:for-each>],
[0<xsl:for-each select="./houses/*">,<xsl:value-of select="."/></xsl:for-each>] );
Mit ihr werden aus den im XML-Dokument angegebenen Planeten- und Hauspositionen zwei anonyme JavaScript-Arrays erzeugt, die dem Konstruktor des HoroSVG-Objekts übergeben werden. Wir "verschwenden" hier den nullten Platz der Arrays, haben dafür aber eine lesbarere Transformation und bekommen für die Häuser sogar einen besser passenden Array. Denn das Element [i] des Arrays entspricht so wirklich dem i. Haus (diesen Luxus, den Platz für das Element Null zu verschwenden, leistet sich, nebenbei bemerkt auch die Swiss Ephemeris).
this.draw = function() {
var lTickMarks = this.getTickMarks();
document.getElementById("tickMarks").setAttribute("d",lTickMarks);
var lSignMarks = this.getSignMarks();
document.getElementById("signMarks").setAttribute("d",lSignMarks);
this.drawPlanets();
this.drawHouses();
this.drawSigns();
};
Schauen wir uns exemplarisch einmal die drawPlanets()-Methode an, die die Planetensymbole an die
richtige Stelle setzt:
this.drawPlanets = function() {
var i, iMax=this.planets.length,
lNode,
lWidth = [13,13,12,13,15,14,11,12,14,15],
lHeight = [15,15,18,18,16,15,14,17,16,14];
for (i=1;i<iMax;i++) {
lNode = document.getElementById("planet"+i);
if (lNode) {
lNode.setAttribute("x",this.polarX(130,this.conv*this.planets[i])-lWidth[i-1]/2);
lNode.setAttribute("y",this.polarY(130,this.conv*this.planets[i])-lHeight[i-1]/2);
}
}
};
Hier werden für jeden Planeten anhand seiner ekliptikalen Länge die x- und y-Koordinate errechnet, an der das Symbol in einem Kreis mit dem Radius 130 zu setzen ist. Da die Symbole zentriert zu setzen sind und nicht alle die gleichen Masse haben, benötigen wir hier noch die Arrays lWidth und lHeight, um eine Korrektur anzubringen.
Ein Firefox-Browser (Release 2.0.0.4) zeigt die resultierende Graphik identisch zu der folgenden PNG-Graphik an (allerdings ohne den schwarzen Rahmen, den ich nur zur Begrenzung eingefügt habe):
Kommen wir nun zur Demonstration des SVG-Prototypen. Es folgt hier im Hypertext dieser Webseite die folgende Angabe:
<object data="horoOutput.xml" type="image/svg+xml" width="800" height="800">
<param name="src" value="horoOutput.xml">
<table style="width:100%;height:100%"><tr><td align="center" valign="middle">
Horoskop kann leider nicht angezeigt werden. Verwenden Sie einen SVG-fähigen
Browser (wie z.B. Firefox). Im Internet Explorer benötigen Sie ein SVG-Plugin,
z.B. den Adobe SVG Viewer.
</td></tr></table>
</object>
Die Datei horoOutput.xml ist dabei das Ergebnis der XSLT-Transformation horo.xsl, wenn man sie auf obiges XML-Dokument horo.xml anwendet. Ich habe sie mit einem kleinen Perl-Script horo.pl unter Verwendung des Moduls XML::XSLT generiert. Wenn Ihr Browser in der Lage ist, SVG darzustellen, sollte eine Graphik angezeigt werden, die genau wie die obige PNG-Datei aussieht:
Wenn Sie hier statt der Graphik den Ersatztext sehen, ist Ihr Browser nicht in der Lage, SVG-Graphiken darzustellen. Wenn Sie zwar eine Graphik sehen, jedoch eine andere als die oben eingefügte PNG-Graphik, so ist SVG zwar im Prinzip darstellbar, bestimmte Features des SVG-Formats (z.B. im Bereich der JavaScript-Anbindung) sind jedoch mangelhaft implementiert.
Ich habe hier einen wesentlichen Schritt vorab manuell ausgeführt, nämlich die XSLT-Transformation. Gemäss SVG-Spezifikation müsste es ebensogut möglich sein, diesen Schritt vom Browser ausführen zu lassen. Statt also im HTML-Code die fertig aufbereitetete SVG-Graphik horoOutput.xml anzugeben, müsste daher ebensogut die Rohdatei horo.xml angegeben werden können. Erst dies würde den ganzen Charme dieser Lösung ausspielen:
<object data="horo.xml" type="image/svg+xml" width="800" height="800">
<param name="src" value="horo.xml">
<table style="width:100%;height:100%"><tr><td align="center" valign="middle">
Horoskop kann leider nicht angezeigt werden. Verwenden Sie einen SVG-fähigen
Browser (wie z.B. Firefox). Im Internet Explorer benötigen Sie ein SVG-Plugin,
z.B. den Adobe SVG Viewer.
</td></tr></table>
</object>
Leider ist es mir auch auf dem Firefox nicht gelungen, diese Version zum Laufen zu bekommen. Ich bin mir noch nicht im Klaren darüber, ob es sich um einen Bug der Firefox-SVG-Implementierung handelt, oder ob ich das Event onload zum Zeichnen der Graphik falsch verwende. Es scheint jedenfalls, dass nach Ausführung der XSLT-Transformation im Browser das Event onload bereits "verpufft" ist, so dass die Methode draw() des HoroSVG Objekts nicht mehr aufgerufen wird.
Die Tatsache, dass ich hier ein Testbild zeige und wie oben kommentiere, spricht für sich: Obwohl bereits ein viele Jahre alter Standard, verbreitet sich das SVG-Format nur schleppend. Es fehlt gewissermassen die "Initialzündung". Solange diese nicht erfolgt, kann auch astrotexte.ch nur auf die guten alten Horoskop-Applets zurückgreifen. Da es in meinen Augen aber ungewiss ist, wie es mit der Java-Technologie clientseitig weitergeht, werde ich den Code für diese Applets nicht weiterentwickeln.
In unserem Fall benötigen wir zum Zeichnen der Planetenpositionen und der Gradmarken am Tierkreis die Möglichkeit, mit Polarkoordinaten zu zeichnen. Polarkoordinaten gehören nicht zum Sprachumfang von SVG. SVG unterstützt nur Angaben in kartesischen Koordinaten. Zwar kann in SVG das gesamte Koordinatensystem gedreht werden – so wie es auch gestreckt oder verschoben werden kann – aber das sind globale Operationen, nicht das, was wir brauchen, um Punkte in der Ebene durch ihre Polarkoordinaten beschreiben zu können.
Auch Sinus und Cosinus sind nicht in den Sprachumfang von XSLT 1.0 eingebaut. Man kann sie zwar einbinden - dazu bedarf es aber einer Extension. Mit den Xalan- oder Saxon-Implementierungen von XSLT kann man auf Java-Klassen, etwa auf java.lang.Math zugreifen, um diesen Mangel von XSLT auszugleichen. Der Preis dafür ist aber, dass man ausserhalb des Standards ist.
Besser ist es daher, wie wir es mit horo.js getan haben, für die Berechnung der Polarkoordinaten auf JavaScript zuzugreifen. Die Einbindung in das XSLT-Programm geschieht mit folgender Zeile (beachten Sie, dass die URL mit XLINK im href-Attribut angegeben werden muss. Ein src-Attribut ist nicht vorgesehen und daher wirkungslos):
<g:script type="text/javascript" xl:href="horo.js"/>
Der Kern der Sprache JavaScript ist als ECMAScript standardisiert, und die gängigen Browser unterstützen diesen Standard ebenso wie einen gewissen gemeinsamen Nenner von DOM-Zugriffsfunktionen. Als browserübergreifende Client-Technologie ist JavaScript die Sprache der Wahl. Dort können wir die Polarkoordinaten schliesslich als Methoden unserer Klasse HoroSVG implementieren:
HoroSVG = function(iPlanets,iHouses) {
...
this.polarX = function(r,phi) {
return Math.round( 100*(200 - r * Math.cos( this.houses[1]-phi )))/100;
};
this.polarY = function(r,phi) {
return Math.round( 100*(200 - r * Math.sin( this.houses[1]-phi )))/100;
};
};
<xsl:processing-instruction name="xml-stylesheet">href="horo.css"Das Stylesheet enthält die folgenden Angaben, im wesentlichen also die Farbgebungen der einzelnen Linien. Es sind aber noch viele andere Stylesheet-Angaben für SVG-Graphik-Elemente möglich. Der Bezug zum SVG-Dokument erfolgt in der für CSS bekannten Selektoren-Syntax.
.line {
stroke-width:1;
stroke:blue;
fill:none;
}
#tickMarks { stroke:red; }
#signMarks { stroke:green; }
So wie ein Chinese sich der Ranges für die chinesischen Schriftzeichen bedient, kann man im HTML-Quelltext auch die astrologischen Sonderzeichen als Unicode-Symbole einsetzen. Beispielsweise könnte ich die Entität
☿als Symbol für Merkur in meinem HTML-Quelltext verwenden. In XML-Dokumenten könnte ich selbst Entitäten mit sprechenden Namen für die Planeten- und Tierkreiszeichensymbole definieren und verwenden, zum Beispiel
&merkur;
Das wäre schön. Aber auch hier sind wir noch weit von idealen Zuständen entfernt. Wenn Sie heutzutage Unicode-Zeichen wie ☿ in Ihren HTML-Quellen verwenden, werden die meisten Betrachter Ihrer Webseiten statt der erwarteten Zeichen ziemlich viele Quadrataspekte zu sehen bekommen (ist nicht das Quadrat ein hemmender Aspekt?). Das Quadrat ist nämlich ein häufig verwendetes Ersatzsymbol für ein nicht implementiertes Unicode-Zeichen. Die wenigsten Benutzer haben auf ihren Rechnern Unicode-Zeichensätze, ganz zu schweigen von solchen, die den Range x2600-x26FF abdecken.
Solange all dies noch Zukunftsmusik ist, müssen wir wie bisher mit Ersatzgraphiken arbeiten – mit allen Nachteilen, die das bietet. Die Zeichen werden nicht in die Zwischenablage übernommen, da sie keinen Text darstellen, sie machen den Quelltext schlechter lesbar, und vor allem skalieren sie nicht mit der Schriftgrösse - jedenfalls nicht automatisch. Zwar könnte man statt der GIF-Dateien, die bei astrotexte.ch für die Planeten- und Tierkreissymbole verwendet werden, immerhin SVG-Dateien verwenden, die mit einem Tool wie Batik etwa aus einem TrueType- oder PostScript-Zeichensatz gewonnen werden können. Aber die Skalierung muss dann durch passende width- und height-Angaben immer noch programmiert werden.
| horo.xml | Daten eines Beispielhoroskops |
| horo.xsl | Die XSLT-Transformation |
| horo.css | Das CSS-Stylesheet |
| horo.js | Die JavaScript-Definiton der Klasse HoroSVG |
use XML::XSLT;
my $xslt = XML::XSLT->new ('horo.xsl', warnings => 1);
$xslt->transform ('horo.xml');
print $xslt->toString;
Viel Spass bei Ihren eigenen Forschungen oder Weiterentwicklungen in dieser Richtung!
| Zum Beginn des Dokuments | Zur Homepage |