svg
TRANSCRIPT
AKADEMIA GÓRNICZO-HUTNICZA
Wydział Elektrotechniki Automatyki Informatyki i Elektroniki
Katedra Telekomunikacji
PRACA MAGISTERSKA
Analiza możliwości wykorzystania grafiki
wektorowej SVG do projektowania aplikacji
internetowych
Paweł Król
Promotor: dr inż. Jacek Romanowski
KRAKÓW 2007
Spis Treści
1. Wstęp...........................................................................................................5
2. Wprowadzenie do standardu SVG...................................................................7
2.1. Podstawowe informacje............................................................................7
2.1.1. Co to jest SVG?.................................................................................7
2.1.2. Czy potrzebujemy kolejnego nowego standardu?.................................8
2.1.3. Historia SVG jako rekomendacji W3C..................................................9
2.1.4. Aktualny stan prac standaryzacyjnych...............................................11
2.2. Rozmaitości...........................................................................................12
2.2.1. Ogólny przegląd standardu...............................................................12
2.2.2. Metody umieszczania grafiki SVG na stronach WWW..........................15
2.2.3. SVG dla urządzeń o niewielkiej mocy obliczeniowej............................16
2.3. Podsumowanie......................................................................................22
3. Szczegółowy przegląd możliwości SVG...........................................................23
3.1. Podstawowe składniki rekomendacji........................................................23
3.1.1. Przedstawienie treści pierwszych rozdziałów......................................24
3.1.2. Zagadnienia renderowania grafiki w SVG...........................................24
3.1.3. Typy danych....................................................................................25
3.1.4. Struktura dokumentu SVG................................................................25
3.1.5. Style...............................................................................................26
3.1.5. Obszar rysunku................................................................................27
3.1.6. Skalowanie w praktyce.....................................................................28
3.1.7. Jednostki wielkości...........................................................................29
3.1.8. Przedstawienie treści kolejnych rozdziałów........................................29
3.1.9. Metadane i kompatybilność wstecz....................................................29
3.1.10. Rozszerzalność języka SVG.............................................................31
3.1.11. Podsumowanie podstawowych informacji........................................31
3.2. Najważniejsze możliwości SVG................................................................32
- 2/141 -
3.2.1. Modelowanie kształtów za pomocą ścieżek........................................32
3.2.2. Wykreślanie figur za pomocą kształtów.............................................34
3.2.3. Wprowadzanie tekstu na rysunku SVG..............................................37
3.2.4. Wypełnienia oraz kontury obiektów...................................................40
3.2.5. Wypełnianie figur za pomocą gradientów i wzorów............................41
3.2.6. Podsumowanie najważniejszych możliwości.......................................46
3.3. Zaawansowane właściwości SVG.............................................................46
3.3.1. Maski oraz ścieżki maskujące............................................................46
3.3.2. Efekty graficzne...............................................................................49
3.3.3. Interaktywność................................................................................53
3.3.4. Hiperłącza.......................................................................................54
3.3.5. Możliwości zastosowania języków skryptowych..................................54
3.3.6. Animacje.........................................................................................57
3.4. Podsumowanie.......................................................................................59
3.4.1. SVG a XML......................................................................................60
3.4.2. Kierunki rozwoju SVG: wersje 1.2 i 2.0..............................................62
4. Praktyczna realizacja przykładowych aplikacji.................................................66
4.1. Generator wykresów statystycznych........................................................66
4.1.1. Zastosowanie aplikacji......................................................................67
4.1.2. Struktura dokumentu SVG................................................................67
4.1.3. Moduł Perla do generowania wykresów.............................................69
4.1.4. Czy SVG jest koniecznością?.............................................................74
4.1.5. Wykres jako skrypt CGI....................................................................76
4.1.6. Możliwości rozbudowy......................................................................77
4.2. Generator wykresów pogodowych...........................................................78
4.2.1. Zalety wykorzystania grafiki wektorowej SVG.....................................79
4.2.2. Format przechowywania danych pogodowych....................................80
4.2.3. Proces transformacji danych arkuszem stylu XSLT..............................84
4.2.4. Przykładowe wykresy.......................................................................93
- 3/141 -
4.2.5. Ocena wykonania aplikacji..............................................................101
4.3. Podsumowanie.....................................................................................102
5. Zakończenie...............................................................................................105
6. Dodatki: kody źródłowe..............................................................................108
6.1. Generator wykresów statystycznych......................................................108
6.1.1. Moduł (klasa) ChartSVG do generowania wykresów.........................108
6.1.2. Porównanie liczby ludności państw świata.......................................114
6.1.3. Porównanie wyników wyborów parlamentarnych..............................117
6.2. Generator wykresów pogodowych.........................................................120
6.2.1. Dane XML do wykresu temperatury.................................................121
6.2.2. Dane XML do wykresu opadów.......................................................124
6.2.3. Arkusz transformacji XSLT..............................................................129
7. Bibliografia.................................................................................................137
8. Słownik trudniejszych pojęć........................................................................138
- 4/141 -
1. Wstęp
Tematem niniejszej pracy dyplomowej jest analiza możliwości wykorzystania grafiki
wektorowej SVG do projektowania aplikacji internetowych. Ponieważ obecnie
trudno wyobrazić sobie funkcjonowanie jakiejkolwiek aplikacji w oderwaniu od
globalnej sieci (brak współpracy z Internetem praktycznie dyskwalifikuje dany
program z powszechnego użycia), w treści swojej pracy szczególny nacisk
zdecydowałem się położyć na staranną analizę samego standardu SVG. Został on
zresztą zaprojektowany jako standard sieciowy, więc niejako sam w sobie zawiera
gotowe już mechanizmy predestynujące go do użycia w środowisku WWW.
Specjalistycznej literatury dedykowanej SVG w języku polskim brak, w związku z
czym podstawą wiedzy, na której oparłem wszystkie swoje informacje na temat
analizowanego standardu, stały się rekomendacje publikowane przez organizację
World Wide Web Consortium. Są one równocześnie najbardziej wiarygodnym
źródłem wiedzy na temat aktualnie obowiązujących w sieci Internet standardów, a
sama rekomendacja SVG została opracowana przez jedną z grup roboczych
wywodzących się z tego właśnie konsorcjum.
Wśród głównych celów, jakie postawiłem sobie podejmując ten właśnie temat
pracy dyplomowej, należy przede wszystkim wymienić dwa następujące:
Chęć gruntownego poznania jednego z najciekawszych obowiązujących
obecnie w sieci standardów grafiki wektorowej, dokładne zapoznanie się z
jego możliwościami od strony technicznej oraz analiza konsekwencji, jakie
niesie ze sobą jego zastosowanie w projektowaniu nowych aplikacji
Realizacja praktycznych przykładów wykorzystujących możliwości SVG,
adaptacja własnych pomysłów na potrzeby wykonania konkretnych zadań,
wreszcie lepsze zrozumienie istoty tworzenia grafiki wektorowej w oparciu o
- 5/141 -
analizowany standard dzięki poznaniu jego możliwości od strony czysto
praktycznej
Aby treść mojej pracy dyplomowej mogła jak najlepiej zaprezentować
najważniejsze informacje dotyczące grafiki SVG, zdecydowałem się podzielić ją na
trzy główne części. Rozdział drugi stanowi wprowadzenie do standardu SVG,
przedstawia w skrócie historię jego powstania oraz zawiera krótkie zestawienie
jego podstawowych funkcjonalności. W rozdziale trzecim zamieszczam szczegółową
analizę większości elementów oferowanych przez standard SVG, przedstawiam
treść rekomendacji W3C oraz prezentuję swoje pomysły oraz wnioski płynące z
analizy kolejnych opisywanych funkcji. Rozdział czwarty stanowi podsumowanie
mojego osobistego wkładu w pracę dyplomową i składa się z części poświęconych
prezentacji konkretnych zastosowań SVG na podstawie zrealizowanych przeze mnie
aplikacji.
Mimo iż niniejsza praca mogłaby mieć charakter czysto teoretyczny, zdecydowałem
się na zupełnie inne podejście. Poszczególne elementy analizowanego standardu
staram się na bieżąco dokumentować samodzielnie opracowanymi praktycznymi
przykładami. Dzięki temu nawet część przeglądowa pracy, opisująca dokładnie
standard SVG, nie składa się wyłącznie z tekstu, ale zawiera również liczne
przykłady kodów źródłowych, prezentujących pewne konkretne funkcjonalności na
bardzo konkretnych przykładach.
Wierzę również, że opracowane przeze mnie aplikacje stanowić będą wraz z
załączonym do pracy ich kodem źródłowym bardzo solidną dawkę praktycznej
wiedzy na temat możliwych zastosowań grafiki wektorowej SVG oraz sposobów ich
realizacji, podkreślający jednocześnie wielkie znaczenie, jakie może ona odegrać w
sieci WWW już w najbliższym czasie.
W tym miejscu pragnąłbym serdecznie podziękować dr inż. Jackowi
Romanowskiemu za życzliwość, okazaną pomoc oraz udzielanie cennych
wskazówek w trakcie pisania tej pracy.
- 6/141 -
2. Wprowadzenie do standardu SVG
W drugim rozdziale swojej pracy dyplomowej chciałbym przedstawić podstawowe
informacje dotyczące standardu SVG. Rozdział ten ma za zadanie stanowić krótkie i
zwięzłe wprowadzenie w świat grafiki wektorowej, dokonany nie od strony
technicznej, lecz od strony organizacyjnej. Chciałbym dokonać w nim ogólnego
przeglądu dokumentacji do standardu SVG, przedstawić pokrótce jego historię oraz
zwrócić uwagę na różnorodność istniejących w jego obszarze profili
przeznaczonych dla rozmaitych urządzeń końcowych. Rozdział został podzielony na
dwie części. W pierwszej z nich przedstawiam najbardziej podstawowe informacje
na temat SVG, w drugiej natomiast zaczynam stopniowo zagłębiać się w bardziej
konkretne tematy i zaawansowane zagadnienia, które kontynuowane i poszerzane
są w rozdziałach kolejnych.
2.1. Podstawowe informacje
Podstawowe informacje przedstawione w pierwszej części drugiego rozdziału
mojej pracy dyplomowej mają za zadanie nakreślić jej czytelnikowi elementarny
i poglądowy szkic standardu SVG. Pokrótce przedstawiam w nim podstawowe
reguły oraz pojęcia z nim związane, prezentuję historię oraz aktualny stan prac
standaryzacyjnych.
2.1.1. Co to jest SVG?
SVG stanowi skrót angielskiego określenia Scalable Vector Graphics, co
oznacza skalowalną grafikę wektorową. SVG posiada status rekomendacji
W3C – W3C (skrót od angielskiego określenia World Wide Web Consortium –
Konsorcjum WWW) jest bardzo wpływową międzynarodową organizacją,
- 7/141 -
zrzeszającą w swoich szeregach przedstawicieli z ponad 400 [1]
najważniejszych firm informatycznych (m.in. AOL, Hewlett-Packard, IBM,
Matsushita, Microsoft, Nokia), organizacji oraz uczelni (m.in. Helsinki
University of Technology, Oxford University, Stanford University, The Hebrew
University of Jerusalem, Universidad Politécnica de Madrid) z całego świata.
W3C zajmuje się rozwijaniem standardów sieciowych od roku 1994, w którym
zostało ono założone przez Tima Bernersa-Lee [2]. Obecnie W3C jest
administrowane wspólnie przez MIT Computer Science and Artificial
Intelligence Laboratory (CSAIL) w USA, European Research Consortium for
Informatics and Mathematics (ERCIM) z siedzibą główną we Francji i Keio
University w Japonii [3].
Podstawowa definicja SVG, przedstawiana na oficjalnej stronie internetowej
organizacji W3C [4], mówi, że SVG jest językiem opisu dwuwymiarowych
obrazów oraz aplikacji graficznych w XML. Aktualnie obowiązującym
standardem jest rekomendacja SVG 1.1 (ustanowiona 14 stycznia 2003 roku).
Nieustannie trwają również prace nad ulepszoną wersją standardu SVG w
wersji 1.2.
Zostały również opracowane pewne ograniczone wersje standardu,
przeznaczone dla telefonii komórkowej trzeciej generacji oraz innych
przenośnych urządzeń, które mocą obliczeniową nie dorównują współczesnym
komputerom stacjonarnym. Cechom charakterystycznym poszczególnych
profili SVG postaram się przyjrzeć bliżej w drugiej części bieżącego rozdziału
mojej pracy.
2.1.2. Czy potrzebujemy kolejnego nowego standardu?
Przede wszystkim SVG nie jest kolejnym nowym standardem. Rekomendacja
SVG w wersji 1.0 została opublikowana 4 września 2001 roku [5], a zatem już
ponad 5 lat temu. Niemniej jednak termin SVG nie zdołał się natychmiastowo
- 8/141 -
wpisać do zbiorowej świadomości użytkowników Internetu. Potrzeba było
czasu, aby szerokie grono twórców (i odbiorców) zainteresowało się poważnie
tym standardem (w niniejszej pracy będę się starał udowodnić, że SVG
zasługuje na znacznie większą uwagę niż obecnie).
W dalszej części rozdziału staram się pokrótce opisać, w jaki sposób
kształtował się standard SVG. Przedstawiam konkretne wydarzenia i
konkretne daty, które miały przełomowe znaczenie w tworzeniu
najważniejszych dla niego dokumentów, dyskutuję na temat aktualnego stanu
prac nad kolejnymi wersjami rekomendacji i wreszcie staram się podkreślić to,
jak ważne jest uczestnictwo wielu różnych firm oraz instytucji w
przygotowywaniu dokumentów stanowiących rekomendacje organizacji W3C.
2.1.3. Historia SVG jako rekomendacji W3C
Już wcześniej została podkreślona waga faktu, że SVG jest rekomendacją
W3C. Aktualnie obowiązującą (kwiecień 2007) wersją standardu jest SVG 1.1
oraz jego uszczuplone profile mobilne (ang. Mobile SVG Profiles): SVG Tiny
oraz SVG Basic [6].
Pierwszą istotną datą w rozwoju nowego standardu grafiki wektorowej jest 11
lutego 1999 roku. W tym dniu został opublikowany pierwszy ogólnodostępny
draft SVG. Prace nad ulepszeniem dokumentu były bardzo intensywne (w
międzyczasie ukazało się jeszcze osiem kolejnych draftów) i trwały do dnia 2
sierpnia 2000 roku, gdy SVG stał się oficjalnym kandydatem do rekomendacji
W3C. W przedstawionym wówczas dokumencie dokonano jeszcze kilku
poprawek i w dniu 5 września 2001 roku SVG 1.0 zostało uznane za oficjalną
rekomendację W3C. W tym samym dniu opublikowano również inny standard:
Animacje SMIL (ang. SMIL Animation), którego głównym celem jest
przygotowanie szkieletu projektowego (ang. framework) pod integrację
animacji z dowolnym dokumentem stworzonym w oparciu o język XML. SVG
- 9/141 -
wykorzystuje Animacje SMIL do tworzenia animowanych efektów na swoich
własnych elementach.
Jak widać prace nad standardem trwały dość długo, bo od pierwszej
propozycji draftu do publikacji oficjalnej rekomendacji minęło ponad 30
miesięcy, a w dokumencie nieustannie dokonywano licznych poprawek.
Świadczy to o tym, iż standard SVG został opracowany bardzo starannie i
rzetelnie przez grono ekspertów, któremu zależało na przygotowaniu
solidnego i bardzo praktycznego dokumentu.
Na miesiąc przed publikacją rekomendacji SVG 1.0, pojawiły się pierwsze
dokumenty, zawierające zestawienie podstawowych wymagań w odniesieniu
do jej następców: SVG w wersji 1.1 oraz 2.0. W tym samym czasie ukazał się
pierwszy draft dokumentu dotyczącego podstawowych wymagań stawianych
profilowi SVG Mobile. 30 października 2001 roku grupa robocza W3C (ang.
W3C Working Group) pracująca nad SVG ogłosiła pierwszą propozycję
specyfikacji 1.1, jak również dwóch dodatkowych profili SVG Tiny i SVG Basic.
14 stycznia 2003 roku dokument, którego opracowanie nadzorowali Jon
Ferraiolo (Adobe Systems), Jun Fujisawa (Canon Inc.) i Dean Jackson (W3C),
stał się oficjalną rekomendacją SVG 1.1, pod którą podpisało się World Wide
Web Consortium.
Przedstawiona historia standardu SVG [9] sięga już kilka lat wstecz. Mimo to
dopiero teraz przeciętni użytkownicy Internetu zaczynają kojarzyć ten
trzyliterowy skrót (SVG) z jego rzeczywistym przeznaczeniem. W prace nad
coraz lepszym dostosowaniem standardu do rosnących wymagań twórców i
użytkowników sieci WWW zaangażowanych jest wielu przedstawicieli
najważniejszych firm informatycznych, uczelni oraz innych instytucji z całego
świata.
- 10/141 -
2.1.4. Aktualny stan prac standaryzacyjnych
Nieustannie trwają prace nad standardem SVG w wersji 1.2. SVG 1.2 jest
aktualnie dostępne w formie draftu [7], zaproponowanego przez Deana
Jacksona z W3C oraz Craiga Northwaya z Canon Inc. Obecnie pracami grupy
przygotowującej nową wersję standardu kieruje Chris Lilley, a w skład
zespołu SVG Working Group wchodzą przedstawiciele takich firm i instytucji
jak Adobe Systems Inc., ERICSSON, France Telecom, Nokia czy Sun
Microsystems Inc. [8].
W związku z przeciągającymi się pracami nad SVG 1.2, szczególny nacisk
położono na szybkie dokończenie i rychłą publikację jego profilu SVG Tiny
1.2. W dniu 10 sierpnia 2006 roku opublikowano oficjalnie dokument SVG
Tiny 1.2 jako kandydata na rekomendację W3C [10]. Uznanie tego standardu
za oficjalną specyfikację sygnowaną przez W3C to kwestia najbliższej
przyszłości. Kolejny draft SVG 1.2 Full ma się ukazać niebawem jako
rozszerzenie SVG Tiny 1.2 o dodatkowe funkcjonalności.
Szczegółowy przegląd oraz porównanie możliwości poszczególnych profili oraz
wersji standardu SVG można znaleźć w drugiej części tego rozdziału. W tym
miejscu chciałbym jeszcze zwrócić uwagę na fakt, że nad przygotowaniem
dokumentu SVG Tiny 1.2 pracowali przedstawiciele tak ważnych firm jak
Adobe Systems (które wycofało się z pracy nad projektem w maju 2006
roku), czy Apple, Canon, Corel Corporation, Hewlett-Packard, IBM, Ikivo,
Macromedia, Microsoft, Motorola, Nokia, Opera Software, Sun Microsystems,
Xerox i wielu innych. Świadczy to o tym, iż standard SVG został uznany za
bardzo istotny element sieci Internet przez wszystkie światowe potęgi
informatyczne. Jednocześnie podkreśla to wagę rekomendacji
opracowywanych przez organizację W3C i konieczność uczestniczenia w
pracach nad nimi przez gigantów branży IT. Zdają oni sobie sprawę z tego jak
ważne jest wywieranie przez nich wpływu na kształt tych rekomendacji.
- 11/141 -
2.2. Rozmaitości
W drugiej części bieżącego rozdziału staram się przedstawić podstawowe
koncepcje, które składają się na będący przedmiotem niniejszej pracy standard.
Oprócz tego poddaję analizie samą nazwę standardu oraz jej implikacje,
wyjaśniam podstawowe pojęcia, z jakimi spotykamy się czytając treść
rekomendacji, by w końcu wskazać kilka sposobów wykorzystania grafiki SVG na
stronach WWW [12]. Na zakończenie rozdziału zwracam uwagę na pewien
podzbiór funkcjonalności SVG, definiowany jako profil przeznaczony dla
urządzeń o niewielkiej mocy obliczeniowej.
2.2.1. Ogólny przegląd standardu
Akronim SVG oznacza Skalowalną Grafikę Wektorową (ang. Scalable Vector
Graphics).
„Skalowalność” polega na możliwości jednolitego zmniejszania oraz
zwiększania rozmiaru. W grafice komputerowej odpowiada to sytuacji, w
której definicja obrazu nie jest ograniczona do pojedynczych pikseli o dobrze
określonej wielkości. Z pojęciem skalowalności spotykamy się również w
sieciach telekomunikacyjnych, gdzie przymiotnik „skalowalny” określa te
techniki, które pozwalają na łatwe przystosowanie usługi zarówno do
niewielkiej, jak i bardzo dużej liczby użytkowników, plików czy aplikacji.
Standard uznaje skalowalność SVG w obu powyższych znaczeniach. Dzięki
temu wyświetlany obraz nie traci na jakości bez względu na to, czy zostanie
on wyświetlony na ekranie niewielkiego urządzenia przenośnego, czy też
wykorzystany do wykonania wydruku na drukarce w najwyższej
rozdzielczości. Obraz może być dowolnie powiększany oraz pomniejszany w
zależności od tego, w jakich okolicznościach jest on przedstawiany. Jeszcze
inną cechą skalowalności SVG jest to, że obrazy SVG mogą stanowić
samodzielne rysunki, jak i być włączane do innych obrazów SVG jako ich
- 12/141 -
części składowe. Dzięki temu skomplikowane ilustracje mogą być budowane z
oddzielnych części nawet przez wiele niezależnie pracujących od siebie osób.
Rys.2.1. Przykładowy wykres pogody na stronie WWW Prognoza Pogody ICM.
Doskonałym przykładem wykorzystania SVG w sieci Internet, który nasuwa mi
się w tym miejscu, może być tworzenie ilustracji, przedstawiającej prognozę
pogody dla wybranego obszaru. Kilka niezależnych zespołów programistów
oraz grafików może mieć przypisane oddzielne zadania. Każdy z nich może
pracować nad ilustracją odpowiadającą innym zbieranym danym: ciśnieniu,
opadom, temperaturze, zachmurzeniu, itp. Następnie wszystkie te fragmenty
łączone są w jeden większy obraz, prezentowany na monitorze użytkownika.
Na stronie internetowej prognozy pogody ICM [11] możemy zobaczyć, jak
- 13/141 -
wygląda taka „modułowa” budowa grafiki (podgląd strony przedstawiłem
również na rysunku 2.1). W tym jednak przypadku nie skorzystano z
możliwości oferowanych przez standard SVG – obrazy przedstawiane są
użytkownikowi w postaci statycznych rysunków w formacie GIF, a co za tym
idzie nie ma możliwości ich bezstratnego powiększania. W tym miejscu
chciałbym zaznaczyć, że jednym z pierwszych celów, jakie postawiłem sobie
do realizacji praktycznej w ramach niniejszej pracy dyplomowej, było
opracowanie aplikacji, umożliwiającej proste generowanie podobnych
wykresów pogodowych, tyle że w formacie SVG.
Kolejną ważną właściwością grafiki SVG jest określenie jej jako „wektorowej”.
Grafika wektorowa zawiera informację o obiektach geometrycznych takich jak
linie proste lub krzywe. Powoduje to, że jest ona zdecydowanie bardziej
elastyczna niż grafika rastrowa (reprezentowana przez obrazy w takich
formatach jak JPG czy PNG), która opiera się na przechowywaniu danych dla
każdego pojedynczego piksela grafiki. Dodatkowo grafika wektorowa posiada
zdolność włączania grafiki rastrowej i jej łączenia z treścią wektorową, np. do
tworzenia ścieżek maskujących (ang. clipping path).
Do pozostałych ważnych cech standardu SVG możemy zaliczyć: fakt, że jest
on aplikacją XML (XML jako rekomendacja W3C jest powszechnie uznanym i
szeroko stosowanym standardem wymiany informacji), za czym idzie
możliwość integracji komponentów SVG w ramach wielu przestrzeni nazw w
większych aplikacjach XML (przykładowo włączając obrazy SVG do
dokumentów napisanych w języku XHTML czy MathML) oraz możliwość
zastosowania arkuszy stylu do kontrolowania wartości atrybutów
poszczególnych elementów dokumentu SVG.
W SVG grafika modelowana jest na poziomie obiektów, a nie pojedynczych
punktów. Podstawowym elementem jest ścieżka (ang. path), która pozwala
na tworzenie rozmaitych obiektów graficznych. Możliwe jest również
- 14/141 -
skorzystanie z kilku podstawowych figur (ang. basic shapes) takich jest
prostokąty czy elipsy.
Standard SVG pozwala użytkownikom na tworzenie nowych, redefiniowanie
istniejących oraz współdzielenie swoich własnych symboli bez konieczności
gromadzenia informacji o nich w scentralizowanych rejestrach. Dzięki temu
różne społeczności użytkowników (np. elektronicy, kartografowie) mogą
tworzyć swoje własne elementy SVG, bez konieczności wprowadzania
dodatkowych modyfikacji do samego standardu.
Bardzo ciekawą propozycją organizacji W3C było wyszczególnienie osobnej
linii rozwojowej standardu SVG, przeznaczonej dla urządzeń mobilnych: profil
SVG Tiny dedykowany telefonom komórkowym oraz profil SVG Basic dla
palmtopów (ang. PDA – Personal Digital Assistant). Specyfikacja SVG Tiny 1.2
posiada status kandydata do rekomendacji (ang. W3C Candidate
Recommendation), a bieżąca wersja draftu została opublikowana w dniu 10
sierpnia 2006 roku. Temat tych uproszczonych wersji standardu SVG
kontynuuję w rozdziale 2.2.3. W związku z faktem, że ani SVG 1.2 Full, ani
SVG 1.2 Tiny nie są jeszcze oficjalnymi rekomendacjami, skupiam się w nim
na krótkiej analizie porównawczej specyfikacji SVG Tiny oraz SVG Basic z SVG
Full, rekomendacjach opublikowanych w dniu 14 stycznia 2003 roku [13].
2.2.2. Metody umieszczania grafiki SVG na stronach WWW
Twórcy grafiki mają do dyspozycji kilka metod, pozwalających na
umieszczanie ich obrazów na stronach internetowych. Wśród możliwych opcji
można wymienić m.in.:
samodzielna (ang. standalone) grafika SVG: w tej sytuacji całkowitą
zawartość strony WWW stanowi dokument SVG, ładowany
bezpośrednio do przeglądarki i tak prezentowany użytkownikowi
osadzanie poprzez odwołanie (ang. embedding by reference): w tym
- 15/141 -
przypadku nadrzędny dokument (strona WWW) odwołuje się do
przechowywanej w oddzielnym pliku grafiki SVG. Takie osadzenie
można wykonać za pomocą jednego z trzech elementów
HTML/XHTML: <img>, <object> lub <applet>
osadzanie wewnętrzne (ang. embedding inline): treść dokumentu SVG
zawarta jest bezpośrednio wewnątrz dokumentu nadrzędnego (np.
strony WWW)
łącze zewnętrzne: wykorzystanie elementu <a> języka HTML
umożliwia przeglądanie grafiki w dowolnym programie akceptującym
format SVG
2.2.3. SVG dla urządzeń o niewielkiej mocy obliczeniowej
SVG Working Group zdecydowało się na opracowanie oddzielnych
rekomendacji dla urządzeń mobilnych przede wszystkim dlatego, że
urządzenia te bardzo znacząco różnią się od siebie w wielu aspektach, między
innymi szybkością procesora (ang. CPU speed), rozmiarem dostępnej pamięci
oraz liczbą wyświetlanych kolorów. Aby skutecznie zaimplementować obsługę
standardu SVG w tak różnorodnych urządzeniach, zdecydowano się na
przygotowanie dla nich oddzielnych profili: SVG Tiny przeznaczone dla
przyrządów o bardzo ograniczonych możliwościach (np. telefonów
komórkowych) oraz SVG Basic dla urządzeń dysponujących nieco większymi
zasobami (np. palmtopy). Podstawowym założeniem obydwu profili jest
umożliwienie renderowania grafiki SVG na ekranach urządzeń przenośnych,
dysponujących ograniczonymi zasobami pamięci czy szybkości procesora.
Na profil SVG Tiny składają się następujące moduły (w obrębie poniższej listy
elementów oraz atrybutów sygnalizuję tylko obecność pewnych
funkcjonalności, szczegółowy opis właściwości wybranych elementów
standardu jest przedmiotem następnego rozdziału):
- 16/141 -
Moduł podstawowych atrybutów (ang. Core Attribute Module),
definiujący zbiór atrybutów (ang. attribute set) Core.attrib, które mogą
wystąpić w dowolnym elemencie. Są to cztery atrybuty: id,
xml:base, xml:lang, xml:space
Moduł podstawowej struktury dokumentu SVG (ang. Basic Structure
Module), w ramach którego zawierają się następujące elementy: defs,
desc, g, metadata, svg, title, use
Moduł podstawowych atrybutów rysowania (ang. Basic Paint Attribute
Module), który definiuje zbiór atrybutów nazwany Paint.attrib, w
którym znajdują się atrybuty takie jak color, fill, stroke,
stroke-width i kilka innych, określających styl rysowania elementów
Moduł podstawowych atrybutów grafiki (ang. Basic Graphics Attribute
Module) to moduł, który definiuje dwa atrybuty: display,
visibility
Moduł hiperłącza (ang. Hyperlinking Module) określa listę dozwolonych
atrybutów elementu a (m.in. Style.attrib, transform, target),
służącego do tworzenia hiperłączy w dokumencie SVG
Moduł atrybutów XLink (ang. XLink Attribute Module) definiuje zbiory
atrybutów: XLink.attrib, XLinkRequired.attrib, XLinkEmbed.attrib i
XLinkReplace.attrib, zawierające atrybuty związane z właściwościami
hiperłączy
Moduł przetwarzania warunkowego (ang. Conditional Processing
Module) zawiera jeden element switch oraz listę dozwolonych dla
niego atrybutów
Moduł figur geometrycznych (ang. Shape Module) opisuje podstawowe
elementy, za pomocą których możemy definiować proste kształty na
- 17/141 -
rysunku SVG. Są to: okrąg (element circle), elipsa (ellipse),
odcinek (line), ścieżka (path), wielokąt (polygon), linia łamana
(polyline) i prostokąt (rect). Każdy z tych elementów posiada listę
dozwolonych atrybutów, które pozwalają określić np. położenie obiektu
w układzie współrzędnych, długości boków lub promień oraz kilka
zestawów atrybutów opisanych w pozostałych podpunktach tego
zestawienia, np. Paint.attrib czy Graphics.attrib
Moduł obrazu (ang. Image Module) opisuje listę dozwolonych
właściwości elementu image, m.in. atrybuty transform, x, y,
width, height oraz kilka innych zestawów atrybutów
Moduł tekstowy (ang. Basic Text Module) zawiera listę dozwolonych
zestawów atrybutów dla elementu text, dozwolone wartości tego
elementu oraz atrybuty wchodzące w skład kolekcji Font.attrib (są to
właściwości opisujące krój, rozmiar oraz styl stosowanej czcionki:
font-family, font-size, font-style, font-weight) oraz
TextContent.attrib (zawierający atrybut text-anchor, określający
wyrównanie tekstu, np. wycentrowanie w poziomie)
Moduł czcionki (ang. Basic Font Module) definiuje listę dozwolonych
atrybutów dla elementów: font, font-face, font-face-name,
font-face-src, glyph, hkern, missing-glyph, służących do
definiowania czcionek
Moduł animacji (ang. Animation Module), definiujący zbiór
podstawowych elementów pozwalających na tworzenie animowanych
efektów w SVG: animate, animateColor, animateMotion,
animateTransform, mpath, set oraz listę skojarzonych z nimi
atrybutów
Moduł rozszerzalności (ang. Extensibility Module) opisuje tylko jeden
- 18/141 -
element foreignObject i jego atrybuty. Zawartością tego elementu
jest zawsze sekcja #PCDATA. Głównym przeznaczeniem tego modułu
jest umożliwienie włączania elementów z innych przestrzeni nazw w
dowolnym miejscu dokumentu SVG.
Powyższe zestawienie modułów daje nam obraz możliwości, jakie powinna
posiadać zgodna ze specyfikacją W3C implementacja profilu SVG Tiny.
Zdecydowałem się na tak dokładne wymienienie wszystkich modułów,
ponieważ stanowią one rdzeń nie tylko dla SVG Tiny, ale również wszystkich
pozostałych profili SVG.
Już teraz SVG Tiny zostało zaimplementowane w ponad 100 modelach
telefonów komórkowych, produkowanych przez takie koncerny jak LG,
Motorola, NEC, Nokia, Panasonic, Samsung, Sharp, Siemens, Sony Ericsson
czy Toshiba [14].
Profil SVG Basic posiada wszystkie cechy SVG Tiny, został on jednak
poszerzony o kilka dodatkowych modułów, dających mu znacznie większe
możliwości. Aby nie wymieniać wszystkich z nich po kolei tak, jak to zrobiłem
dla SVG Tiny, pozwolę sobie przedstawić pokrótce te, które uważam za
najistotniejsze i które decydują o tym, że profil SVG Basic jest znacznie
bardziej skomplikowany. Co za tym idzie, jest on implementowany w
urządzeniach mobilnych typu palmtopy, które posiadają większe możliwości
przetwarzania danych niż zwykłe aparaty telefoniczne.
Pierwszym modułem, na który chcę zwrócić uwagę, jest moduł stylów (ang.
Style Module), który pozwala na zastosowanie arkuszy stylu CSS lub XSL do
dokumentu SVG. Dzięki temu w samym dokumencie SVG możemy
zdefiniować tylko i wyłącznie „treść” rysunku (np. dane wykresu), natomiast
jego wygląd (np. kolory poszczególnych słupków na wykresie) możemy
kontrolować z poziomu zewnętrznego arkusza stylu.
Inną cenną własnością profilu SVG Basic jest implementacja modułu
- 19/141 -
atrybutów przezroczystości (ang. Opacity Attribute Module). Moduł ten
pozwala na definiowanie właściwości określających przezroczystość do
wszystkich elementów, które w wykazie swoich atrybutów posiadają zbiór
Opacity.attrib.
Jedną z najbardziej wyróżniających się różnic pomiędzy profilami SVG Basic i
SVG Tiny jest implementacja modułu gradientowego (ang. Gradient Module),
który pozwala na definiowanie gradientów liniowych oraz radialnych w SVG
Basic. W związku z faktem, że rysowanie gradientów wymaga znacznych
nakładów obliczeniowych, moduł ten nie mógłby być zaimplementowany w
SVG Tiny.
Moduł wzorów (ang. Pattern Module) pozwala na wykorzystanie
predefiniowanych obiektów złożonych z podstawowych elementów SVG jako
wypełnienia innych elementów rysunku. Pozwala to użytkownikowi na
definiowanie dowolnie skomplikowanych wzorów, a następnie ich
zastosowanie na przykład jako wypełnienia podstawowych figur.
W SVG Basic wprowadzone zostają dodatkowe moduły, odpowiedzialne za
interaktywność rysunku. Pierwszym z nich jest moduł atrybutów zdarzeń
dokumentu (ang. Document Events Attribute Module), który pozwala m.in.
określać, jak ma zachować się przeglądarka w przypadku nie załadowania
któregoś z elementów, niekompletnego załadowania elementu, skalowania
obrazu, przewijania obrazu w poziomie lub w pionie oraz jego powiększania
lub pomniejszania. Drugim interaktywnym modułem jest moduł atrybutów
zdarzeń elementów graficznych (ang. Graphical Element Events Attribute
Module), definiujący atrybuty dedykowane elementom graficznym obrazu
SVG, które pozwalają określić sposób reakcji przeglądarki na zdarzenia takie
jak naciśnięcie lub zwolnienie przycisku myszy nad wybranym elementem,
przemieszczanie wskaźnika myszy nad elementem, itp. Trzeci zbiór atrybutów
interaktywnych zdefiniowany jest w module atrybutów zdarzeń animacji (ang.
- 20/141 -
Animation Events Attribute Module). Moduł ten pozwala zdefiniować reakcje
na zdarzenia takie jak rozpoczęcie animacji, jej zakończenie oraz powtórzenie.
Bardzo ważnym modułem jest moduł skryptów (ang. Scripting Module).
Definiuje on tylko jeden element script, który posiada jednak potężne
możliwości. Przeznaczenie tego elementu jest dokładnie takie samo jak w
przypadku dokumentów HTML, tzn. pozwala on na zamieszczanie skryptów
wewnątrz dokumentów SVG (z tego też powodu zawartość tego elementu
może stanowić wyłącznie sekcja #PCDATA). Zastosowanie skryptów jest
szczególnie przydatne wówczas, gdy chcemy manipulować obrazem SVG w
wyniku zaistniałych zdarzeń. Banalny przykład: korzystając z obiektowego
modelu dokumentu SVG, możemy wywołać prosty skrypt, który w wyniku
kliknięcia myszą na elemencie, spowoduje zmianę jego rozmiarów. Możliwości
zastosowania skryptów są ogromne, ponieważ SVG umożliwia niezależne
kojarzenie zdarzeń z dowolnym elementem graficznym rysunku.
Ostatnim modułem, na który chciałbym zwrócić uwagę w tym skróconym
opisie, jest moduł podstawowych filtrów (ang. Basic Filter Module). Filtry są
doskonałym narzędziem do tworzenia efektów graficznych, urozmaicają
rysunek, sprawiają, że zawartość grafiki staje się o wiele bogatsza w stosunku
do prymitywnego oryginału. Filtrowanie poszczególnych części rysunku
pozwala na uzyskanie takich efektów graficznych jak rozmycie, przesunięcie,
rozświetlenie, łączenie różnych warstw, morfing, turbulencja i inne.
Oprócz wymienionych wyżej modułów, które decydują o tym, że SVG Basic
jest zdecydowanie bardziej skomplikowane niż SVG Tiny (chociaż ciągle nie
tak trudne w implementacji jak SVG Full), dostępnych jest jeszcze kilka innych
modułów, o których nie wspomniałem, by nie zagłębiać się w niezbyt ciekawe
szczegóły teorii.
Mimo iż są ograniczone w stosunku do SVG Full, to jednak zarówno SVG Tiny
jak i SVG Basic posiadają potężne możliwości tworzenia oraz manipulowania
- 21/141 -
obrazami. Co istotne, obie wersje mobilnego standardu SVG są obecnie
skutecznie implementowane w coraz większej liczbie urządzeń przenośnych
[14], na co już wcześniej zwróciłem uwagę.
2.3. Podsumowanie
W drugim rozdziale swojej pracy dyplomowej dokonałem krótkiego
wprowadzenia do standardu SVG, przedstawiłem podstawowe koncepcje oraz
pojęcia z nim związane, opisałem historię jego powstania oraz przebieg prac
standaryzacyjnych. Zwróciłem również uwagę na kilka zagadnień praktycznych,
takich jak umieszczanie grafiki SVG na stronach WWW. Na jego zakończenie
przybliżyłem elementy składowe okrojonych profili dyskutowanego standardu,
które przeznaczone są do implementacji w urządzeniach mobilnych ustępujących
mocą obliczeniową współczesnym komputerom stacjonarnym.
W rozdziale trzecim zamierzam przedstawić szczegółowo składniki rekomendacji
SVG w ostatniej opublikowanej wersji 1.1, dokonać precyzyjnej analizy
najważniejszych możliwości grafiki SVG oraz zaprezentować kilka konkretnych
przykładów jej wykorzystania. Z kolei rozdział czwarty poświęcam
charakterystyce aplikacji przygotowanych przeze mnie na potrzeby pracy oraz
demonstracji praktycznych ich możliwości.
- 22/141 -
3. Szczegółowy przegląd możliwości SVG
Trzeci rozdział pracy dyplomowej chciałbym poświęcić szczegółowemu przeglądowi
możliwości grafiki SVG. Poprzedni rozdział w sposób bardzo ogólny opisywał
podstawowe jej cechy. Sucha teoria, nie poparta konkretnymi przykładami, może
dać zaledwie przybliżony obraz potencjalnych możliwości dyskutowanej techniki.
Dlatego też ten rozdział będzie o wiele bardziej konkretny, zawierać będzie kilka
praktycznych przykładów i wartościowych, z praktycznego punktu widzenia,
informacji. Zdaję sobie sprawę z tego, że nie sposób ogarnąć wszystkich istotnych
właściwości skalowalnej grafiki wektorowej (nie mówiąc już o ogarnięciu standardu
SVG jako całości) w jednym krótkim rozdziale (potrzeba by na to
kilkusetstronicowej książki, a i ona zapewne nie wyczerpałaby do końca tematu,
nad którym pracuje od wielu lat szerokie grono ekspertów). Postaram się zatem
zwrócić uwagę tylko na te zagadnienia, które z punktu widzenia osoby chcącej
poznać od podstaw grafikę SVG mogłyby się wydać najciekawsze, a których
znajomość będzie jednocześnie doskonałym punktem wyjścia do dalszego
poszerzania wiedzy.
3.1. Podstawowe składniki rekomendacji
Wszystkie właściwości grafiki SVG można odnaleźć w rekomendacji W3C [15].
Kolejne rozdziały specyfikacji dotyczą różnorodnych aspektów tego standardu.
Celem przejrzystego zestawienia jego podstawowych cech pozwolę sobie
dokonać w niniejszym rozdziale krótkiej charakterystyki zagadnień, opisywanych
w kilku różnych rozdziałach specyfikacji, a dotyczących najbardziej
podstawowych obszarów wykorzystania grafiki SVG (rozdział trzeci mojej pracy
- 23/141 -
w całości został oparty na informacjach zawartych w rekomendacji W3C).
3.1.1. Przedstawienie treści pierwszych rozdziałów
Pierwszy rozdział rekomendacji wprowadza nas w świat SVG, wyjaśniając
podstawowe pojęcia i terminy stosowane w tekście specyfikacji, jak również
przedstawiając kilka kluczowych zagadnień z zakresu jej praktycznego
wykorzystania (przyjęte rozszerzenia plików SVG, typ MIME, przestrzeń nazw
SVG, deklarację DTD dokumentu).
W drugim rozdziale opisane zostają kluczowe koncepcje standardu:
wyjaśniona zostaje geneza nazwy SVG, jej najważniejsze idee, jak również
możliwości wykorzystania grafiki SVG w sieci WWW (o zagadnieniach tych
pisałem już wcześniej w rozdziałach 2.3.1 oraz 2.3.2).
3.1.2. Zagadnienia renderowania grafiki w SVG
Rozdział trzeci dotyczy szczegółów technicznych związanych z renderowaniem
grafiki SVG. Jest to krótki, ale bardzo ważny rozdział, ponieważ procedura
generowania rysunku determinuje to, w jaki sposób widzieć go będzie na
ekranie swojego monitora użytkownik końcowy. Kolejność nakładania
następujących po sobie elementów na prezentowany obraz ma bardzo istotne
znaczenie. Na przykład nie jest do końca oczywiste, czy elementy potomne
powinny być rysowane na pierwszym czy na drugim planie w stosunku do
swoich elementów nadrzędnych. Rekomendacja reguluje wszelkie tego typu
niejasności.
W rozdziale szczegółowo opisano, jak powinny być renderowane grupy
elementów, jak pojedyncze elementy, w jaki sposób nakładane mają być filtry
na wykreślone już wcześniej regiony, czy też jak realizować maskowanie oraz
nakładanie się różnych elementów. Informacje te są szczególnie istotne dla
osób i firm, zajmujących się implementacją standardu SVG w swoich
- 24/141 -
własnych aplikacjach, zwłaszcza w przeglądarkach internetowych oraz
edytorach graficznych.
3.1.3. Typy danych
W rozdziale czwartym rekomendacji przedstawione zostały bardzo
szczegółowe informacje na temat podstawowych typów danych oraz
interfejsów DOM stosowanych w SVG. Można dowiedzieć się stąd między
innymi o tym, że właściwości oraz atrybuty elementów SVG mogą mieć
przypisane wartości m.in. następujących typów: liczby całkowite oznaczane
jako <integer>, liczby rzeczywiste <number>, długości <length>
(dozwolone jednostki opisane zostały w rozdziale 7.10 specyfikacji),
współrzędne <coordinate>, listy wartości <list of xxx>, kąty
<angle>, kolory <color>, procenty <percentage>, częstotliwości
<frequency>, czas <time> oraz kilka innych.
Bardzo wartościową, zwłaszcza dla osób tworzących grafikę SVG w sposób
niebezpośredni (tzn. nie w specjalizowanych do tego edytorach graficznych,
ale na przykład za pośrednictwem innych języków programowania lub za
pomocą zwykłego edytora tekstu), jest sekcja zawierająca zestawienie
rozpoznawalnych jako kluczowe nazw kolorów. Przeglądając kod źródłowy
dokumentu SVG o wiele łatwiej można sobie wyobrazić wygląd rysunku, jeżeli
kolory opisane są przez wyrazy gold i brown zamiast rgb(255,215,0) i
rgb(165,42,42). Kolorów, które posiadają w SVG swoje nazwy, jest 147 –
jest to ilość zadowalająca nawet bardziej wymagających twórców grafiki.
3.1.4. Struktura dokumentu SVG
Rozdział piąty dotyczy struktury dokumentu SVG. Opisano w nim, w jaki
sposób poprawnie zdefiniować dokument SVG za pomocą elementu <svg>
oraz odpowiedniego zapisu przestrzeni nazw w elemencie nadrzędnym.
- 25/141 -
Zawarte zostały tam również informacje na temat dozwolonych atrybutów
elementu <svg>. W dalszej części rozdziału scharakteryzowane zostało
zagadnienie grupowania większej liczby elementów w jeden za pomocą
elementu <g>.
Kolejne zagadnienia dotyczą odwołań do innych zasobów za pomocą
identyfikatorów URI (ang. Uniform Resource Identifiers), zastosowania
elementów opisowych, przetwarzania warunkowego oraz innych kwestii
związanych z poprawną konstrukcją dokumentów SVG. Na zakończenie
rozdziału zestawione zostały wszystkie moduły SVG, które dotyczą
wspomnianych zagadnień, m.in. moduł atrybutów dozwolonych dla
wszystkich elementów, moduł struktury zawierający listę elementów
związanych ze strukturą dokumentu, moduł przetwarzania warunkowego
(ang. Conditional Processing Module) czy moduł obrazów (ang. Image
Module).
3.1.5. Style
O możliwościach zastosowania stylów w SVG można przeczytać w rozdziale
szóstym rekomendacji W3C. Style pozwalają na opisanie wielu różnorodnych
właściwości dokumentu SVG, przede wszystkim na zdefiniowanie sposobu, w
jaki renderowane będą elementy rysunku. Pozwalają na określanie takich
parametrów jak wypełnienia, kontury, grubości linii. W przypadku tekstu
pozwalają na przykład na określenie rozmiaru oraz rodzaju czcionki.
Modyfikują parametry, które wpływają na sposób generowania rysunku, takie
jak ścieżki maskujące, znaczniki czy filtry.
Właściwości wszystkich elementów rysunku SVG mogą zostać określone bez
definiowania stylów (czasami takie rozwiązanie jest konieczne, na przykład
gdy ten sam plik SVG przetwarzany jest za pomocą różnych programów, z
których nie każdy obsługuje zewnętrzne style), ale najczęściej stosuje się w
- 26/141 -
tym celu jeden z dwóch standardów: XSLT oraz CSS. Podejścia do
zagadnienia są tu jednak różne.
W przypadku zastosowania XSLT jesteśmy w stanie dokonać skomplikowanej
transformacji dowolnego dokumentu z danymi w formacie XML (a zatem
także rysunku SVG) do dowolnego innego formatu, na przykład SVG
(przykładem takiego zastosowania jest jedna ze zrealizowanych przeze mnie
aplikacji praktycznych, opisana w rozdziale zatytułowanym „Generator
wykresów pogodowych”).
Technika CSS pozwala nam na zastosowanie stylów do już istniejących
dokumentów SVG, dokładnie w taki sam sposób, jak stosuje się ją do
dokumentów HTML. Skojarzony z dokumentem SVG arkusz stylu CSS pozwala
na modyfikację atrybutów wszystkich elementów tego dokumentu.
Rekomendacja wskazuje szczegółowo, w jaki sposób można z poziomu
dokumentu SVG odwoływać się do zewnętrznego arkuszu stylu oraz jakie
reguły rządzą przetwarzaniem dokumentu SVG za pomocą techniki XSLT.
3.1.5. Obszar rysunku
Rozdział siódmy został poświęcony systemowi współrzędnych rysunku,
transformacjom oraz stosowanym jednostkom. Bardzo ważnym pojęciem, na
które zwraca uwagę rekomendacja, jest tutaj obszar obrazu (ang. canvas).
Obszar obrazu definiowany jest przez specyfikację jako „przestrzeń, na której
renderowana jest treść rysunku”. Oprócz tego definiowany jest również
widoczny obszar obrazu (ang. viewport), który określany jest jako „widoczna
dla użytkownika część obrazu”. W odniesieniu do tych terminów
definiowanych jest mnóstwo innych, określających rozmiary obrazu,
współrzędne rysunku, matryce transformacji, itd.
Przyjmuje się, że punkt w lewym górnym rogu rysunku ma współrzędne
(0,0). Przesuwając się w prawo wzdłuż osi X wartości rosną, podobnie
- 27/141 -
przesuwając się w dół wzdłuż osi Y wartości również rosną. Oczywiście za
pomocą odpowiednich transformacji możliwe jest przemieszczanie, obracanie,
przekrzywianie oraz skalowanie pierwotnego systemu współrzędnych i co za
tym idzie modyfikowanie prezentowanego użytkownikowi obrazu.
3.1.6. Skalowanie w praktyce
Bardzo ciekawym parametrem elementu <svg>, odnoszącym się do
skalowania, jest atrybut viewBox. Umożliwia on dopasowanie rozmiarów
rysunku do potrzeb użytkownika. W ten sposób, jeśli posiadamy rysunek o
oryginalnych rozmiarach 2000x2000 pikseli i chcemy, aby został on „ściśnięty”
na naszej stronie WWW do obszaru o wymiarach 150x200 pikseli (bo akurat
taki rozmiar jest wymagany przez układ graficzny naszej strony), wystarczy
zastosować atrybut viewBox, który spowoduje, że oryginalny obraz zostanie
przeskalowany i w całości wypełni obszar przeznaczony dla rysunku SVG:
<svg width="150px" height="200px" version="1.1"viewBox="0 0 2000 2000" preserveAspectRatio="none"xmlns="http://www.w3.org/2000/svg">
Analogicznie do poprzedniego przykładu, jeżeli ten sam rysunek na innej już
stronie potrzeba by umieścić w obszarze o wymiarach 250x100 pikseli,
zapisalibyśmy:
<svg width="250px" height="100px" version="1.1"viewBox="0 0 2000 2000" preserveAspectRatio="none"xmlns="http://www.w3.org/2000/svg">
W powyższym przykładzie w elemencie <svg> zdefiniowano jeszcze jeden
dodatkowy atrybut: preserveAspectRatio. Służy on do określenia, czy
rysunek ma zachować oryginalne proporcje, czy też może on całkowicie
wypełnić przeznaczony mu obszar. Lista dostępnych wartości tego parametru
jest długa i pozwala na bardzo precyzyjny dobór warunków skalowania
- 28/141 -
rysunku.
3.1.7. Jednostki wielkości
Innym interesującym zagadnieniem, poruszanym w siódmym rozdziale
rekomendacji, jest wspomniana wcześniej kwestia jednostek. W SVG wszelkie
współrzędne oraz rozmiary mogą być definiowane zarówno z jak i bez
identyfikatora jednostek. Jeżeli liczby podawane są bez jednostki, przyjmuje
się, że wartość podana jest w jednostkach użytkownika. Możliwe jest również
wskazanie którejś z jednostek absolutnych zdefiniowanych przez standard
CSS (tj. em, ex, px, pt, pc, cm, mm, in). Oprócz tego dozwolone jest także
określanie rozmiarów w procentach.
3.1.8. Przedstawienie treści kolejnych rozdziałów
Kilka kolejnych rozdziałów standardu dotyczy zagadnień, którym szczegółowo
przyjrzę się w rozdziale 3.2 pracy zatytułowanym „Najważniejsze możliwości
SVG”, ponieważ dotyczą one bezpośrednio tworzenia konkretnych elementów
SVG, takich jak ścieżki (rozdział 8 rekomendacji), podstawowe kształty
(rozdział 9), tekst (rozdział 10), wypełnienia i kontury (rozdział 11), gradienty
i wzory (rozdział 13) oraz czcionki (rozdział 20).
Bardziej skomplikowane właściwości SVG, takie jak przycinanie, maskowanie,
komponowanie (rozdział 14), filtry (rozdział 15), interaktywność (rozdział 16),
hiperłącza (rozdział 17), skrypty (rozdział 18) oraz animacje (rozdział 19)
będą przedmiotem rozdziału 3.3 niniejszej pracy zatytułowanego
„Zaawansowane właściwości SVG”. W tym miejscu pozostały mi do omówienia
jeszcze trzy ostatnie rozdziały specyfikacji.
3.1.9. Metadane i kompatybilność wstecz
Rozdział dwudziesty pierwszy dotyczy metadanych w SVG. Standard XML
- 29/141 -
pozwala na opisywanie danych przez inne dane nazywane dla rozróżnienia
metadanymi i zawieranie tych informacji wewnątrz samego dokumentu. W
związku z tym SVG posiada również taką możliwość, która realizowana jest za
pomocą elementu <metadata>.
W rozdziale dwudziestym drugim opisano dwa scenariusze, pozwalające na
zachowanie kompatybilności wstecz dokumentom SVG. Oczywistym jest, że
użytkownik końcowy może korzystać z takiej wersji przeglądarki internetowej
lub innego programu (ang. user agent), która nie została wyposażona w
obsługę standardu SVG (taka obsługa została/zostanie dopiero
zaimplementowana w jego kolejnej wersji lub w ogóle). W związku z tym
założenie, że rysunek SVG zostanie poprawnie wyświetlony na każdym
urządzeniu końcowym, jest błędne. Rekomendacja W3C proponuje
przeciwdziałać niekompatybilności starszych programów z nowym standardem
na dwa sposoby.
W przypadku dokumentów zgodnych ze standardem XML, które pozwalają na
osadzanie treści SVG, można użyć elementu <switch> w następujący
sposób, aby zapewnić alternatywny rysunek do wyświetlenia na wypadek,
gdyby program nie obsługiwał formatu SVG:
<switch> <!-- Wyświetl rysunek SVG, jeżeli jest to możliwe. --> <ref type="image/svg+xml" src="rysunek.svg"/> <!-- W przeciwnym razie wyświetl alternatywny rys. --> <img src="alternatywny_rysunek.jpg"/></switch>
W przypadku HTML 4, rysunki SVG mogą być osadzane w dokumencie za
pomocą elementu <object> w następujący sposób:
<object type="image/svg+xml" data="rysunek.svg"> <!-- Jeżeli przeglądarka internetowa nie jest w stanie wyświetlić rysunku SVG, wówczas przetwarzana będzie
- 30/141 -
poniższa zawartość: --> <img src="alternatywny_rysunek.jpg" alt="opis"></object>
3.1.10. Rozszerzalność języka SVG
Ostatni rozdział rekomendacji opisuje zagadnienia związane z rozszerzalnością
(ang. extensibility) języka SVG. Możemy się z niego dowiedzieć, że SVG
pozwala na włączanie elementów z obcych przestrzeni nazw w dowolnym
miejscu dokumentu. SVG dostarcza również mechanizmów pozwalających
innym językom XML na wstawianie swojej zawartości na obszar rysunku (np.
wstawianie wyrażenia matematycznego przygotowanego w języku MathML na
wybrany fragment rysunku). Służy do tego celu element <foreignObject>,
który przyjmuje cztery atrybuty określające położenie obcego elementu na
rysunku SVG (współrzędne x i y oraz rozmiary width i height).
W SVG możliwe jest również dodawanie własnych elementów oraz atrybutów
do składni DTD. W ten sposób użytkownik może zbudować w oparciu o
standard SVG swój własny rozszerzony język SVG, który najlepiej będzie
spełniał jego osobiste potrzeby.
3.1.11. Podsumowanie podstawowych informacji
Rekomendacja opisuje bardzo szczegółowo wymienione w tym rozdziale
mojej pracy zagadnienia. Moim celem nie było powielanie tych informacji, lecz
solidne ich streszczenie oraz dokonanie ogólnego przeglądu możliwości grafiki
SVG. Chciałem zasygnalizować potężne możliwości tego języka, jak również
przedstawić dokładniej kilka zagadnień, które wydały mi się najciekawsze. W
rozdziale 3.2 zaprezentuję na konkretnych przykładach w postaci kodu
źródłowego oraz wygenerowanych w przeglądarce internetowej rysunków, w
jaki sposób korzystać z podstawowych elementów graficznych oferowanych
przez standard oraz jak wygląda tworzenie grafiki w oparciu o te elementy.
- 31/141 -
3.2. Najważniejsze możliwości SVG
Przedmiotem niniejszego rozdziału jest przedstawienie najistotniejszych z punktu
widzenia twórcy grafiki możliwości, oferowanych przez standard SVG. W celu
realizacji tego zadania omówię oraz przedstawię kilka praktycznych przykładów
wykorzystania podstawowych elementów, będących składnikami języka SVG.
Mam nadzieję, że rozdział ten da znacznie szerszy obraz możliwości
dyskutowanego standardu niż omawiane do tej pory zagadnienia teoretyczne.
3.2.1. Modelowanie kształtów za pomocą ścieżek
Pierwszym elementem, na który chciałbym zwrócić uwagę w tym paragrafie,
jest ścieżka (ang. path). Rozdział ósmy rekomendacji W3C definiuje ścieżkę
jako kontur (ang. outline) figury, który może zostać wykorzystany na jeden z
trzech sposobów (lub jako ich kombinację): wypełnienie, obramowanie,
ścieżka maskująca. Ścieżka opisywana jest za pomocą koncepcji bieżącego
punktu (ang. current point) – co jest przez to rozumiane, łatwo sobie
wyobrazić stosując anologię z rysowaniem na papierze. Bieżący punkt jest
odpowiednikiem aktualnego położenia ołówka na kartce, po której rysujemy.
Jego pozycja może ulec zmianie w dowolnym momencie, kontur może być
zamknięty lub otwarty i wykreślany za pomocą linii krzywych lub prostych.
Ścieżka reprezentuje kontur obiektu, zdefiniowany za pomocą takich instrukcji
jak moveto (określenie nowej pozycji dla bieżącego punktu), lineto
(wykreślenie linii prostej do wskazanego punktu), curveto (wykreślenie linii
krzywej do wskazanego punktu przy wykorzystaniu miary Beziera), arc
(poprowadzenie łuku eliptycznego lub kołowego) oraz closepath
(zamknięcie kształtu poprzez przedłużenie ścieżki do ostatniego z elementów
moveto).
Ścieżki w SVG definiowane są za pomocą elementu <path>. Dozwolone są tu
następujące dwa atrybuty: d zawierający definicję konturu oraz pathLength
- 32/141 -
określający całkowitą długość ścieżki, służący do jej skalowania przez
aplikację. Kluczowym składnikiem jest tu atrybut d elementu <path>,
definiujący kształt ścieżki. Jego wartością jest wyrażenie, składające się z
instrukcji sformułowanych za pomocą pojedynczych liter, np. litera M
odpowiada instrukcji moveto, a litera L odpowiada instrukcji lineto. Po
instrukcji następują liczby, stanowiące parametry instrukcji (ich liczba zależy
od typu instrukcji), a po nich kolejne instrukcje. Całość wyrażenia jest
definicją ścieżki.
Na rysunku 3.2 przedstawiono przykładowy obraz SVG, utworzony wyłącznie
z elementów <path>, natomiast na rysunku 3.1 można przejrzeć kod
źródłowy SVG, który posłużył do jego wygenerowania. Jak widzimy do ścieżek
można stosować wszystkie podstawowe atrybuty, pozwalające na określanie
takich właściwości utworzonych figur jak chociażby kolor wypełnienia czy
grubość obramowania. Czyni to ścieżki jednymi z najefektywniejszych
narzędzi do edycji grafiki wektorowej.
<path d="M 10 280 L 180 10 L 140 260 Z" fill="yellowgreen" stroke="darkgreen" stroke-width="5" stroke-linejoin="round"/><path d="M 300 100 h -80 a 80 80 0 1 0 80 -80 Z" fill="khaki" stroke="peru" stroke-width="5"stroke-linejoin="square"/><path d="M 10 10 l 50 50 a 25 25 30 0 1 50 25 l 75 25 a 25 50 30 0 1 50 50 l 25 50 a 25 100 40 0 1 50 50 l 50 25" fill="none" stroke="sienna" stroke-width="6" stroke-linejoin="square"/><path d="M 10 230 Q 60 10 180 180 T 390 250" fill="none"stroke="dodgerblue" stroke-width="6" stroke-linecap="round"/>
Rys.3.1. Kod źródłowy obrazu SVG, przedstawionego na rysunku 3.2(pominięto nagłówek XML, deklarację DTD oraz element główny <svg>).
Starałem się na jednym obrazie zamieścić różne kształty wygenerowane za
pomocą tego samego elemetu SVG, tj. ścieżki <path>, aby pokazać jak
- 33/141 -
różnorodne i wszechstronne zastosowanie może mieć ten jeden tylko element
standardu.
Rys.3.2. Rysunek SVG utworzony wyłącznie z elementów <path>.
3.2.2. Wykreślanie figur za pomocą kształtów
Rozdział 9 rekomendacji W3C poświęcony został opisowi podstawowych
kształtów (ang. basic shapes), oferowanych użytkownikowi przez standard
SVG. Poza ścieżkami są one chyba najważniejszymi z podstawowych
elementów grafiki SVG. Odróżnia ich od ścieżek to, że są dużo łatwiejsze do
zastosowania dla początkujących użytkowników.
Specyfikacja oferuje następujące słowa kluczowe służące do definiowania
podstawowych kształtów: <rect> (prostokąt), <circle> (koło),
- 34/141 -
<ellipse> (elipsa), <line> (linia), <polyline> (linia łamana) i
<polygon> (wielokąt). Mimo iż każdy z tych kształtów, dla których
stworzono oddzielne słowa kluczowe, można uzyskać przez odpowiednie
zdefiniowanie ścieżki <path>, o wiele wygodniejsze wydaje się przecież
wykreślanie prostokąta za pomocą elementu <rect> niż poprzez
wykorzystanie ścieżki <path>. Rzut oka na kod źródłowy pozwala
zorientować się, w którym miejscu znajduje się definicja prostokąta, w
przypadku zastosowania nie opatrzonego żadnym komentarzem elementu
<path> nie jest to w żadnym wypadku oczywiste.
Każdy z wymienionych wyżej kształtów może posiadać kontur, zdefiniowany
za pomocą odpowiednich atrybutów (stroke, stroke-width, itp.). Może
również zostać wypełniony (atrybut fill). Wiąże się to z faktem, że każdy z
atrybutów dostępnych dla elementu <path> można zastosować również do
każdego z podstawowych kształtów.
Na rysunku 3.3 zamieszczono przykładowy obraz, na którym wykreślono po
jednym obiekcie dla każdego z elementów zawierających się w zbiorze
podstawowych kształtów. Warto zauważyć, że atrybuty poszczególnych
elementów są bardzo intuicyjne i możliwe do zrozumienia nawet bez
dogłębnej lektury specyfikacji. Atrybuty x i y zwykle oznaczają współrzędne
obiektu na płaszczyźnie – nie inaczej jest w tym przypadku. Atrubuty width
oraz height odpowiadają odpowiednio szerokości oraz wysokości obiektu, r
to oczywiście promień koła, natomiast rx i ry to dwa promienie elipsy. W
przypadku linii musimy określić współrzędne jej początku oraz końca, a zatem
zastosujemy zmienne z indeksem: x1 i y1 oraz x2 i y2. Jedynym wyjątkiem
są tu elementy <polyline> oraz <polygon>. Zbiór kolejnych punktów
figury stanowi wartość atrybutu points (współrzędne punktów podawane są
w układzie współrzędnych użytkownika). Każdy z wymienionych elementów
- 35/141 -
można ujrzeć w kodzie źródłowym obrazu zamieszczonym na rysunku 3.4.
Rys.3.3. Rysunek SVG utworzony za pomocą podstawowych kształtów.
<rect x="20" y="180" width="180" stroke-width="5" height="100" fill="paleturquoise" stroke="darkcyan"/><circle cx="330" cy="100" r="50" fill="darkseagreen" stroke="darkgreen" stroke-width="5"/><ellipse cx="135" cy="50" rx="120" ry="30" fill="palegoldenrod" stroke="brown" stroke-width="5"/><line x1="220" y1="285" x2="280" y2="15" stroke="olivedrab" stroke-width="5"/><polyline fill="none" stroke="steelblue" stroke-width="5" points="20,110 30,155 125,155 135,110 225,110 235,155"/><polygon fill="orange" stroke="firebrick" stroke-width="5" stroke-linejoin="round" points="316,175 332,218 380,218 342,245 357,288 316,262 275,288 290,245 250,218 300,218"/>
- 36/141 -
Rys.3.4. Kod źródłowy obrazu SVG, przedstawionego na rysunku 3.3.
3.2.3. Wprowadzanie tekstu na rysunku SVG
W rekomendacji SVG duży nacisk położony został nie tylko na elementy
związane bezpośrednio z wstawianiem elementów graficznych na rysunkach.
Bardzo rozbudowane zostały również rozdziały poświęcone wykreślaniu
tekstu. Rozdział dziesiąty poświęcony został w całości metodom wstawiania
tekstu na rysunek, natomiast rozdział dwudziesty traktuje o możliwościach
zastosowania różnych czcionek oraz stylów do wykreślanego tekstu.
Do umieszczania tekstu na rysunkach SVG służy element <text>. Tekst,
który ma zostać wyświetlony na ekranie, powinien się znaleźć pomiędzy
znacznikiem otwierającym oraz zamykającym element <text>.
Element tekstowy traktowany jest w dokładnie identyczny sposób jak
wszystkie pozostałe elementy SVG. Konsekwencją tego faktu jest to, że
wszelkie transformacje współrzędnych (będzie o nich mowa w rozdziale 3.3),
nakładanie, maskowanie czy wypełnianie kolorami ma dokładnie takie samo
zastosowanie w odniesieniu do tekstu jak do podstawowych kształtów oraz
ścieżek.
Aby wstawić nowy obiekt tekstowy na rysunek należy w elemencie <text>
określić jego położenie za pomocą atrybutów x oraz y oznaczających jego
bezwzględne współrzędne. Atrybut fill definiuje kolor znaków. Wyboru
czcionki dokonuje się za pomocą atrybutu font-family, jej rozmiaru
natomiast poprzez wartość atrybutu font-size. Istnieje możliwość
dostosowywania właściwości czcionki (kolor, wyróżnienie, indeksowanie, itp.)
oraz tekstu (zawijanie wierszy, zaawansowane formatowanie, itp.) w obrębie
jednego ciągu znaków poprzez zastosowanie kilku elementów <tspan>,
przyjmujących identyczne argumenty, zawartych wewnątrz pojedynczego
- 37/141 -
elementu <text>. Za pomocą atrybutu text-anchor można określić
wyrównanie tekstu w odniesieniu do jego współrzędnych (wyrównanie do
lewej, do prawej strony lub wyśrodkowanie osiąga się odpowiednio
wartościami start, end oraz middle).
Do ustalenia właściwości czcionki służą między innymi atrybuty: font-style
pozwalający na wybór pomiędzy pismem normalnym, ukośnym oraz kursywą,
font-weight służący do określenia poziomu pogrubienia liter, font-
stretch umożliwiający wskazanie pożądanego ściśnięcia bądź rozrzedzenia
przestrzeni pomiędzy znakami.
Rys.3.5. Wykreślanie tekstu w grafice SVG. Na rysunku widoczny jestfragment zaznaczony poprzez przeciągnięcie nad nim wskaźnika myszy.
- 38/141 -
Oprócz wspomnianych właściwości w stosunku do elementów <text> oraz
<tspan> można zastosować wiele różnorodnych efektów: rotację znaków,
zróżnicowane odstępy pomiędzy literami oraz słowami, podkreślanie oraz
przekreślanie tekstu, zmianę jego orientacji (co jest przydatne zwłaszcza w
przypadku stosowania niektórych języków azjatyckich), czy na przykład
umieszczanie tekstu na ścieżce, dzięki czemu można tworzyć fantazyjnie
wyglądające napisy.
Bardzo ważną cechą elementów tekstowych w SVG jest to, że z punktu
widzenia użytkownika, przeglądającego rysunek, są one traktowane nie jako
obiekty graficzne, lecz jako tekst. Oznacza to, że użytkownik musi mieć
możliwość zarówno zaznaczenia wybranego fragmentu tekstu, jak i jego
skopiowania do schowka systemowego (ang. clipboard).
Na rysunku 3.6 zaprezentowano kilka sposobów wstawiania tekstu oraz jego
formatowania na rysunku SVG. Efekt zastosowania przedstawionego na nim
kodu źródłowego można zobaczyć na rysunku 3.5. Warto zwrócić uwagę na
fragment tekstu wyróżniony za pomocą wskaźnika myszy – widzimy, że
przeglądarka „Opera” ma możliwość zaznaczania oraz kopiowania tekstu z
rysunku SVG, zalecaną w rekomendacji W3C.
<text x="5" y="30" font-family="Verdana" font-size="22" fill="dodgerblue">As far as the laws of <tspan font-weight="bold" fill="magenta">mathematics</tspan></text><text x="200" y="80" font-family="Verdana" font-size="48" fill="navy" font-style="italic" text-anchor="middle">refer to reality</text><text x="390" y="120" font-family="Verdana" font-size="32" fill="maroon" text-decoration="underline" text-anchor="end">they are not</text><text font-family="Verdana" font-size="64" fill="purple"><tspan x="10 40 80 130 190 270 350" y="185">certain</tspan></text><defs><path id="MyPath" d="M 30 290 S 50 200 120 200 S 180 280 250 280 S 390 240 390 240"/></defs><text font-family="Verdana" font-size="16"
- 39/141 -
fill="darkslategrey"><textPath xlink:href="#MyPath"> As far as they are certain, they do not refer to reality...</textPath></text>
Rys.3.6. Kod źródłowy obrazu SVG, przedstawionego na rysunku 3.5.
3.2.4. Wypełnienia oraz kontury obiektów
Rozdział jedenasty specyfikacji SVG został poświęcony rysowaniu wypełnień
(ang. fill) oraz konturów (ang. stroke). Każdy element ścieżki, tekstu oraz
podstawowych kształtów, omówiony oddzielnie w trzech poprzednich
rozdziałach może być wypełniony (wypełnienie rozumiane jest tutaj jako
zamalowanie wnętrza obiektu) oraz obrysowany (co oznacza wykreślenie
konturu wokół obiektu). Obie te operacje, tzn. wypełnianie oraz
obrysowywanie, razem określane są ogólniejszym terminem rysowania (ang.
painting). W grafice SVG możemy rysować (tzn. wypełniać obszary oraz je
obrysowywać) za pomocą:
pojedynczego koloru
pojedynczego koloru z określoną przezroczystością
gradientu (liniowego lub kołowego)
wzoru (wektorowego lub innego rysunku, również sąsiadująco (ang.
tiled))
innych obiektów zdefiniowanych poprzez rozszerzenia SVG
Atrybuty fill oraz stroke przyjmują ten sam typ wartości, którym może
być: słowo kluczowe none (które oznacza, że operacja rysowania nie jest
wykonywana), nazwa koloru (jedna z niemal 150 dozwolonych nazw,
zebranych w czwartym rozdziale rekomendacji, np. gold lub brown), wartość
koloru (zapisana w formacie #RRGGBB, np. #FFD700 lub #A52A2A) lub
odnośnik URI (ten sposób stosowany jest dla gradientów, wzorów oraz
- 40/141 -
obiektów rozszerzonych, np. jeśli w obrębie tego samego dokumentu
zdefiniowano gradient i oznaczono go identyfikatorem myGradient,
wówczas odwołujemy się do niego pisząc url(#myGradient)).
Dostępne są jeszcze dwie dodatkowe opcje definiowania sposobu wypełniania
elementów. Atrybut fill-rule decyduje o tym, które fragmenty obszaru
rysowania należy uznać za znajdujące się wewnątrz obiektu, a więc dla której
części elementu ma zostać narysowane wypełnienie (dla większości
standardowych obiektów nie ma on jednak większego znaczenia). O wiele
bardziej atrakcyjny z punktu widzenia użytkownika wydaje się być atrybut
fill-opacity. Określa on poziom przezroczystości wypełnienia z zakresu
od 0 (całkowita przezroczystość) do 1 (wypełnienie nieprzezroczyste).
W przypadku rysowania konturów dostępnych jest o wiele więcej opcji.
Wspomniany już atrybut stroke definiuje kolor rysowania. Atrubut stroke-
width pozwala na dobór grubości linii, którą wykonywany jest obrys. Mamy
możliwość dokonania wyboru, czy zakończenie linii ma być kwadratowe czy
może zaokrąglone, dobierając odpowiednio wartość atrubytu stroke-
linecap. Podobnie rzecz ma się z połączeniami linii, stanowiących krawędzi
figur lub ścieżek – można je łączyć ze sobą na trzy sposoby: ostry (ang.
miter), skośny (ang. bevel) oraz zaokrąglony (ang. round), przypisując
pożądany typ połączenia atrybutowi stroke-linejoin. Mamy również
możliwość wykreślania linii kreskowanych, kropkowanych oraz dowolnych ich
kombinacji poprzez ustawienie wartości atrybutu stroke-dasharray.
Podobnie jak w przypadku wypełnienia, tak również do konturu można
zaaplikować dowolny stopień przezroczystości. Do tego celu służy atrybut
stroke-opacity (domyślnie zarówno kontur jak i wypełnienie są całkowicie
nieprzezroczyste).
Specyfikacja dostarcza również możliwości zakańczania ścieżek oraz linii
- 41/141 -
symbolami strzałek. Aby z niej skorzystać należy użyć elementu <marker>.
3.2.5. Wypełnianie figur za pomocą gradientów i wzorów
Jedne z najefektowniejszych, a jednocześnie wymagających niewielkich
nakładów pracy ze strony twórcy, rezultatów zastosowania grafiki SVG można
osiągnąć, korzystając z gradientów oraz wzorów, opisanych szczegółowo w
trzynastym rozdziale specyfikacji. Już wcześniej wspominałem, że SVG
pozwala na wypełnianie kształtów jak i obrysowywanie ich konturów nie tylko
za pomocą pojedynczego koloru, ale również za pomocą gradientów i
wzorów. Przyjrzymy się teraz szerzej tym możliwościom.
Czym w ogóle jest gradient? W grafice SVG przez gradient rozumiane jest
płynne przejście pomiędzy dwoma kolorami. Standard definiuje dwa rodzaje
gradientów: liniowy (ang. linear gradient) oraz kołowy (ang. radial gradient),
określane odpowiednio za pomocą elementów <linearGradient> oraz
<radialGradient>. Po zdefiniowaniu gradientu w dokumencie, można się
do niego odwoływać poprzez atrybuty fill i stroke wybranego elementu,
aby wskazać chęć jego wypełnienia bądź narysowania konturu za pomocą
określonego gradientu.
Wśród najważniejszych atrybutów służących do definiowania gradientów
liniowych możemy wyróżnić: współrzędne x1, y1 oraz x2, y2 (współrzędne
te określają punkt początkowy oraz punkt końcowy gradientu, najczęściej
określane są w procentach, domyślnie przypisane są im następujące wartości:
x1="0%", y1="0%", x2="100%", y2="0%"), identyfikator id (unikalny
identyfikator, który można przypisać każdemu elementowi dokumentu SVG, a
zatem również gradientowi, umożliwiający późniejsze odwoływanie się do
niego poprzez ten identyfikator) i spreadMethod (pozwalający określić
zachowanie gradientu, kończącego się wewnątrz). Kolory, pomiędzy którymi
dokonywane jest przejście gradientowe, definiuje się wewnątrz elementu
- 42/141 -
<linearGradient> za pomocą co najmniej dwóch elementów podrzędnych
<stop>, w których określone zostają wartości dwóch kluczowych atrybutów:
stop-color odpowiadający pożądanemu kolorowi oraz offset (wyrażany
głównie w procentach) służący do określenia położenia tego koloru w
gradiencie.
W przypadku gradientu kołowego dostępne atrybuty różnią się nieznacznie,
ponieważ jego charakter jest trochę inny niż gradientu liniowego. Zamiast
współrzędnych definiujących punkt początkowy oraz punkt końcowy mamy
tutaj możliwość zdefiniowania współrzędnych największego możliwego okręgu
(cx, cy, r), który będzie wchodził w skład gradientu (gradient zostanie
wykreślony w ten sposób, że jego kolor określony dla atrybutu
offset="100%" w elemencie <stop> będzie odpowiadał właśnie temu
największemu okręgowi). Z kolei punkt środkowy, od którego gradient
„promieniuje” definiuje się za pomocą atrybutów fx i fy, które określają
środek okręgu (punkt ten odpowiada kolorowi określonemu w elemencie
<stop> dla atrybutu offset="0%").
W grafice SVG efektownie rysować można nie tylko za pomocą gradientów,
ale również za pomocą samodzielnie zdefiniowanych wzorów (ang. patterns).
Poprzez wzór możemy rozumieć niezależny rysunek SVG, który można później
dowolnie wykorzystać. Przygotowanie wymyślnego wzoru, a następnie
wypełnienie nim wnętrza jakiegoś elementu może nieraz dać zadziwiający
efekt.
Do definiowania wzorów służy element <pattern>. Odwoływać się można
do niego w atrybutach fill i stroke dokładnie w taki sam sposób jak do
gradientów (patrz przykład na rysunku 3.8). Podstawowymi atrybutami
elementu <pattern> są: współrzędne x i y, szerokość width oraz
wysokość height (parametry te decydują, w jaki sposób zostaną
- 43/141 -
rozmieszczone poszczególne kafelki (ang. tiles) wzoru), a także
patternUnits (definiujący system współrzędnych dla wymienionych
atrybutów) i patternContentUnits (definiujący system współrzędnych dla
wszystkich elementów składających się na wzór). Wewnątrz elementu
<pattern> możemy zdefiniować nasz wzór dokładnie w taki sam sposób,
jakbyśmy definiowali niezależny rysunek SVG. Wzór może się składać ze
ścieżek, prostokątów, okręgów oraz wszelkich innych elementów SVG.
Jedynym ograniczeniem jest tu tylko inwencja twórcy.
Na rysunku 3.7 został przedstawiony obraz, na którym zamieszczono kilka
podstawowych elementów SVG, do których wypełnienia oraz obrysowania
zastosowano różnorodne techniki i właściwości opisane powyżej, włączając w
to gradienty, wzory, przezroczystości, zdefiniowane przez użytkownika
kreskowanie linii i inne. Kod źródłowy, który posłużył do wygenerowania tej
grafiki, został przedstawiony na rysunku 3.8.
- 44/141 -
Rys.3.7. Rysunek SVG prezentujący zastosowanie różnych technikwypełniania oraz obrysowywania kształtów, między innymi za pomocą
gradientów i wzorów.
- 45/141 -
<defs> <linearGradient id="MyGradient1" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="#00FF00"/><stop offset="100%" stop-color="#0000FF"/></linearGradient> <radialGradient id="MyGradient2" gradientUnits="userSpaceOnUse" cx="190" cy="120" r="80" fx="190" fy="120"><stop offset="0%" stop-color="yellow"/><stop offset="100%" stop-color="red"/></radialGradient> <linearGradient id="MyGradient3" x1="0%" y1="0%" x2="100%" y2="0%"><stop offset="0%" stop-color="dodgerblue"/><stop offset="100%" stop-color="purple"/></linearGradient> <pattern id="MyPattern1" patternUnits="userSpaceOnUse" x="0" y="0" width="10" height="10" viewBox="0 0 10 10"><path d="M 0 4 L 10 0 L 6 10 z" fill="red" stroke="black"/><path d="M 0 4 L 6 10 L 0 10 z" fill="yellow" stroke="black"/><path d="M 10 0 L 6 10 L 10 10 z" fill="lightgreen" stroke="black"/></pattern> </defs><path d="M -50 230 Q 100 10 200 170 T 480 30" fill="none" stroke="gold" stroke-width="120" stroke-linecap="square" stroke-opacity="0.3"/><text x="50" y="275" font-family="Verdana" font-size="112" font-weight="bold" fill="url(#MyGradient1)" stroke-dasharray="9,4" stroke="black" stroke-width="2">SVG</text><path d="M 20 270 Q 40 120 150 50 T 320 190 T 380 30" fill="none" stroke="url(#MyGradient3)" stroke-width="15"stroke-linecap="square"/><ellipse cx="45" cy="65" rx="30" ry="50" fill="url(#MyPattern1)" stroke="brown" stroke-width="5" fill-opacity="0.2" stroke-dasharray="2,1" stroke-opacity="0.2"/><ellipse cx="330" cy="65" rx="30" ry="50" fill="url(#MyPattern1)" stroke="brown" stroke-width="5" fill-opacity="0.8" stroke-dasharray="2,1"/><circle cx="190" cy="100" r="40" stroke-width="5" fill="url(#MyGradient2)" stroke="darkgreen"/><circle cx="155" cy="135" r="40" stroke-width="5" fill="url(#MyGradient2)" stroke="darkred"/><circle cx="215" cy="135" r="40" stroke-width="5" fill="url(#MyGradient2)" stroke="darkblue"/>
Rys.3.8. Kod źródłowy obrazu SVG, przedstawionego na rysunku 3.7.
- 46/141 -
3.2.6. Podsumowanie najważniejszych możliwości
W rozdziale tym starałem się przedstawić najważniejsze z możliwości
oferowanych przez standard SVG. Znalazło się wśród nich wykreślanie
podstawowych kształtów, wyświetlanie tekstu na rysunku, wypełnianie oraz
ograniczanie obszarów, zarządzanie kolorami oraz stosowanie gradientów i
wzorów. Każde z zagadnień zostało poparte praktycznym przykładem, który
składał się z kodu źródłowego oraz podglądu wygenerowanego za
pośrednictwem przeglądarki internetowej obrazu. W następnym rozdziale
swojej pracy przyjrzę się bliżej bardziej zaawansowanym technikom SVG, nie
pomijając wyjaśnienia tak istotnych zagadnień jak filtry, interaktywność
elementów, hiperłącza, skrypty oraz animacje.
3.3. Zaawansowane właściwości SVG
Wśród zaawansowanych właściwości SVG, które są przedmiotem szczegółowej
analizy w niniejszym rozdziale, można odnaleźć kwestie związane z
maskowaniem, nakładaniem czy przycinaniem fragmentów rysunku,
zastosowaniem różnorodnych efektów filtrujących, opcjami interaktywności
poszczególnych elementów, możliwościami zastosowania hiperłączy w
odniesieniu do konkretnych składników grafiki, elementami umożliwiającymi
realizowanie animowanych sekwencji na rysunkach, a także sposobami
wykorzystania języków skryptowych do realizacji zadań związanych z obsługą
zdarzeń.
3.3.1. Maski oraz ścieżki maskujące
Zagadnienia maskowania są przedmiotem czternastego rozdziału
rekomendacji W3C. SVG oferuje użytkownikowi dwa podstawowe elementy
maskujące: ścieżki maskujące (ang. clipping paths) oraz maski (ang. masks).
Ścieżki maskujące składają się z dowolnej kombinacji podstawowych
- 47/141 -
elementów SVG (ścieżek, kształtów, tekstu), które pełnią rolę ramki (ang.
outline) dla maski w ten sposób, że wszystko co znajduje się wewnątrz ramki
zostaje pokazane, natomiast reszta (wszystko co znajduje się na zewnątrz
ramki) nie. Z kolei maski są obiektami mogącymi zawierać dowolne elementy
graficzne, które zostają użyte jako półprzezroczysta maska, umożliwiająca
przede wszystkim komponowanie obiektów pierwszoplanowych do aktualnego
tła. Najważniejsze rozróżnienie pomiędzy ścieżkami maskującymi oraz
maskami polega na tym, że te pierwsze mogą znajdować się tylko w jednym z
dwóch stanów: albo całkowitej przezroczystości, albo całkowitej
nieprzezroczystości. Maski natomiast zawierają obraz, w którym każdy
pojedynczy piksel może być determinowany przez inny stopień
przezroczystości.
Ścieżkę maskującą definiuje się za pomocą elementu <clipPath>. Jego
elementami potomnymi mogą być tylko następujące obiekty: ścieżki <path>,
napisy <text>, podstawowe kształty (np. <rect>), a także elementy
<use>, które mogą odwoływać się jedynie do tych trzech rodzajów
wymienionych obiektów. Każdy element graficzny SVG może odwoływać się
do ścieżki maskującej poprzez atrybut clip-path. Wartością tego atrybutu
może być none (brak maskowania) lub odwołanie URI do obiektu w ramach
tego samego dokumentu SVG, który będzie pełnił rolę ścieżki maskującej.
Każdy obiekt graficzny w SVG może zostać użyty jako maska do
wkomponowania innego obiektu w tło rysunku. Do definiowania maski służy
element <mask>. Odwołujemy się do niej za pomocą tak samo nazwanego
atrybutu mask. Przykłady zastosowania obu metod maskowania można
zobaczyć na rysunku 3.9. Przedstawiono na nim między innymi napis
maskowany za pomocą gradientu. Na rysunku jako pierwszy rysowany jest
jasnozielony prostokąt. Następnie nanoszony jest na niego napis SVG,
zawierający odwołanie do wcześniej zdefiniowanej maski. Tekst zostaje
- 48/141 -
wypisany w kolorze ciemnozielonym (widać to dobrze w kodzie źródłowym po
wartości atrybutu wypełnienia: fill="darkgreen"), ale zostaje do niego
zaaplikowana maska zawierająca definicję gradientu przezroczystości (atrybut
odwołania do maski w kodzie źródłowym: mask="url(#Mask1)").
Rys.3.9. Rysunek SVG prezentujący zastosowanie różnych technikmaskowania (dla ułatwienia rozpoznania ścieżki maskującej, narysowane
zostały zarówno kontury tej ścieżki, jak i wypełniony został oryginalny kształtnie zamaskowanego obiektu) oraz dwóch przykładowych filtrów feTurbulence
i feComponentTransfer.
Dzięki temu lewa strona napisu jest prawie niewidoczna (całkowicie zlewa się
z kolorem tła, ponieważ maska jest w tym miejscu nieprzezroczysta),
natomiast prawa strona posiada dokładnie taki kolor jak zdefiniowano w
elemencie <text> (jest dobrze widoczna, ponieważ w tym miejscu maska
- 49/141 -
jest całkowicie przezroczysta). Kod źródłowy tego dokumentu SVG został
zamieszczony na rysunku 3.10.
<defs> <clipPath id="MyClip1"><path d="M 20 20 L 250 50 L 180 160 Z" clip-rule="evenodd"/></clipPath> <linearGradient id="Gradient1"><stop offset="10%" stop-color="white" stop-opacity="0"/><stop offset="90%" stop-color="white" stop-opacity="1"/></linearGradient> <mask id="Mask1"><rect x="160" y="180" width="220" height="100" fill="url(#Gradient1)"/></mask> <filter id="Turbulence1"><feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="5"/></filter> <filter id="ComponentTransfer1"><feComponentTransfer><feFuncR type="linear" slope=".25" intercept=".25"/><feFuncG type="linear" slope=".50" intercept=".25"/><feFuncB type="linear" slope=".75" intercept=".50"/></feComponentTransfer></filter></defs><rect clip-path="url(#MyClip1)" x="20" y="30" width="220" height="100" fill="goldenrod"/><rect x="160" y="180" width="220" height="100" fill="lightgreen"/><text x="270" y="265" font-family="Verdana" font-weight="bold" font-size="96" text-anchor="middle" fill="darkgreen" mask="url(#Mask1)">SVG</text><rect x="30" y="161" width="100" height="108" filter="url(#Turbulence1)"/><ellipse cx="325" cy="90" rx="55" ry="70" fill="url(#Gradient1)" filter="url(#ComponentTransfer1)"/>
Rys.3.10. Kod źródłowy obrazu SVG, przedstawionego na rysunku 3.9.
3.3.2. Efekty graficzne
Kolejną bardzo cenną właściwością grafiki SVG jest możliwość zastosowania
różnorodnych filtrów, pozwalających na generowanie niezwykle wyszukanych
efektów. Filtry oferowane przez SVG są zwykle dostępne w zaawansowanych
programach graficznych, co czyni je jednymi z najpotężniejszych narzędzi
- 50/141 -
wchodzących w skład tego standardu. Rozdział piętnasty specyfikacji,
poświęcony właśnie filtrom, jest bardzo rozbudowany i szczegółowo omawia
zastosowanie każdego z dostępnych efektów, zawiera również kody źródłowe
prezentujące przykładowe ich wykorzystanie.
Standardowo działanie filtru polega na wykonaniu określonych operacji na
źródle danych i wytworzeniu na ich podstawie nowych danych wyjściowych,
które stanowią ich modyfikację. Dokładnie tak samo funkcjonują filtry w SVG,
przy czym danymi źródłowymi są tu dane grafiki (od pojedynczego elementu
aż po grupy obiektów), natomiast wyjściem filtru jest obraz, jaki otrzymuje na
ekranie swojego monitora użytkownik końcowy.
Filtry definiowane są za pomocą elementu <filter>. Aby dany efekt został
zastosowany do wybranego obiektu, należy ustawić wartość jego atrybutu
filter tak, aby wskazywała na identyfikator zdefiniowanego wcześniej
filtru.
Każdy filtr zawiera zbiór prymitywów (ang. filter primitives), z których każdy
może być jednym z jego elementów podrzędnych. Każdy prymityw wykonuje
na danych wejściowych jedną fundamentalną operację graficzną, np.
rozmycie (ang. blur) lub efekt oświetlenia (ang. lighting effect), w wyniku
której uzyskuje się wyjściowe dane graficzne.
Dwa przykłady zastosowania filtrów zostały przedstawione na rysunku 3.9,
natomiast kod źródłowy prezentujący sposób ich definiowania został
zamieszczony na rysunku 3.10.
Szczegółowa analiza wszystkich prymitywów wykracza poza ramy tej pracy.
Niemniej jednak chciałbym pokrótce wymienić ich kompletną listę wraz z
krótka charakterystyką uzyskiwanych w ich rezultacie efektów, aby odnieść
się do postawionej wcześniej tezy, że filtry są jednymi z najpotężniejszych
narzędzi oferowanych przez standard SVG. Oto pełna lista opisanych w
rekomendacji prymitywów filtrów:
- 51/141 -
feBlend: prymityw nakłada dwa obiekty na siebie, używając jednego z
popularnych trybów mieszania obrazów (ang. image blending mode)
feColorMatrix: prymityw aplikuje matrycę transformacji do każdego
piksela wejściowej grafiki, w rezultacie której otrzymujemy nowy zbiór
kolorów w zapisie RGBA
feComponentTransfer: prymityw wykonuje odwzorowanie danych
składowych koloru dla każdego piksela, umożliwiając operacje takie jak
korekta jasności, regulacja kontrastu, balans kolorów czy progowanie
(ang. thresholding)
feComposite: prymityw dokonuje połączenia dwóch obrazów na
poziomie pojedynczych pikseli
feConvolveMatrix: prymityw zastosowania matrycy splotu łączy ze
sobą sąsiednie piksele, aby wytworzyć obraz wynikowy; poprzez splot
można uzyskać wiele interesujących efektów graficznych takich jak
rozmycie, detekcja krawędzi (ang. edge detection), wyostrzanie (ang.
sharpening), gofrowanie (ang. embossing) czy ukosowanie (ang.
beveling)
feDiffuseLighting: prymityw oświetla rysunek wykorzystując kanał
przezroczystości (ang. alpha channel) do mapowania wypukłości (ang.
bump map); obliczenia są zgodne ze standardem odbicia światła w
modelu oświetlenia Phonga; w rezultacie działania filtru uzyskiwany jest
obraz nieprzezroczysty
feDisplacementMap: prymityw wykorzystuje wartości pikseli z
rysunku podanego jako wartość atrybutu in2 do przestrzennego
przesunięcia (ang. spatial displacement) obrazu z in
feFlood: prymityw tworzy prostokąt wypełniony kolorem podanym w
- 52/141 -
atrybucie flood-color oraz przezroczystością podaną w atrybucie
flood-opacity
feGaussianBlur: prymityw realizuje rozmycie gaussowskie (ang.
Gaussian blur)
feImage: prymityw odwołuje się do zewnętrznej grafiki (którą może być
obraz rastrowy lub rysunek SVG)
feMerge: prymityw nakłada na siebie kolejne warstwy rysunków
(„jedna nad drugą”) podanych jako jego argumenty wejściowe
feMergeNode: prymityw nakładający nową warstwę rysunku
feMorphology: prymityw wykonuje „pogrubianie” (ang. fattening) lub
„odchudzanie” (ang. thinning) rysunku; jest przydatny w szczególności w
odniesieniu do kanału przezroczystości
feOffset: prymityw przesuwa (ang. offset) wejściowy rysunek
względem jego bieżącej pozycji w przestrzeni obrazu (ang. image space)
o określony wektor; jest on szczególnie przydatny do generowania
efektów takich jak rzucanie cienia (ang. drop shadow)
feSpecularLighting: prymityw oświetla rysunek wykorzystując kanał
przezroczystości do mapowania wypukłości; obliczenia są zgodne ze
standardem odbicia światła w modelu oświetlenia Phonga; w rezultacie
działania filtru uzyskiwany jest obraz przezroczysty
feTile: prymityw wypełnia wskazany prostokąt powtarzalnym wzorem,
którym jest rysunek, podany jako parametr wejściowy filtru
feTurbulence: prymityw tworzy rysunek na podstawie funkcji
turbulencji Perlina, umożliwiającej generowanie sztucznych tekstur
przypominających wyglądem chmury, marmur, itp.
feDistantLight: prymityw definiujący kierunkowe źródło światła
- 53/141 -
fePointLight: prymityw definiujący punktowe źródło światła
feSpotLight: prymityw definiujący reflektorowe źródło światła
feFuncR: funkcja przenoszenia (ang. transfer function) dla czerwonego
komponentu wejściowej grafiki
feFuncG: funkcja przenoszenia dla zielonego komponentu wejściowej
grafiki
feFuncB: funkcja przenoszenia dla niebieskiego komponentu wejściowej
grafiki
feFuncA: funkcja przenoszenia dla komponentu przezroczystości (ang.
alpha component) wejściowej grafiki
Na podstawie powyższej listy jesteśmy w stanie śmiało stwierdzić, że
rzeczywiście za pomocą filtrów możemy definiować niezwykle wyrafinowane
efekty graficzne, dzięki którym nasze obrazy nie muszą w niczym ustępować
wymyślnym rysunkom stworzonym w zaawansowanych edytorach
graficznych.
3.3.3. Interaktywność
Elementy SVG mogą być interaktywne, tzn. mogą reagować na zdarzenia
inicjowane przez użytkownika, dzięki następującym właściwościom:
inicjowane przez użytkownika akcje, takie jak na przykład wciśnięcie
lewego przycisku myszy, mogą spowodować rozpoczęcie wykonywania
animacji lub skryptów
użytkownik ma możliwość inicjować hiperłącza do innych stron
internetowych poprzez takie działania jak kliknięcie lewego przycisku
myszy nad konkretnym elementem grafiki
w wielu przypadkach (zależy to od możliwości przeglądarki internetowej
- 54/141 -
jak i od ustawień atrybutu zoomAndPan w elemencie głównym <svg>)
użytkownik jest w stanie powiększać oraz przesuwać obraz SVG
ruchy myszą mogą powodować zmianę wyglądu wskaźnika (ang.
pointing device)
Rozdział szesnasty rekomendacji W3C szczegółowo opisuje informacje na
temat dostępnych zdarzeń (łącznie z warunkami definiowanymi za pomocą
atrybutu pointer-events, które muszą być spełnione, aby dane zdarzenie
mogło zostać wywołane), sposobu sprawdzenia czy dany dokument może być
powiększany oraz przesuwany (ang. panned), jak również sposobu wyboru
bieżącego wskaźnika myszy (wraz z listą dostępnych wartości
przekazywanych poprzez atrybut cursor).
3.3.4. Hiperłącza
Do definiowania każdego rodzaju łącz SVG korzysta ze standardu XLink. W
rozdziale siedemnastym specyfikacji zostały omówione łącza zewnętrzne,
które tworzy się za pomocą podobnego jak w języku HTML elementu <a>.
Służy on do wskazywania innych elementów, które będą stanowić hiperłącze.
Przykładowy kod łącza do zewnętrznego zasobu sieciowego przedstawiono na
rysunku 3.11. W tym przykładzie wykreślony zostaje prostokąt. Po kliknięciu
na nim użytkownik przechodzi na internetową stronę wyszukiwarki Google.
Hiperłącze może jednak wskazywać na dowolny inny zasób sieciowy, np.
rysunek, film, program, inny dokument SVG, stronę HTML i inne.
<a xlink:href="http://www.google.pl"> <rect x="0" y="0" width="10" height="10" fill="green"/></a>
Rys.3.11. Kod źródłowy łącza do zewnętrznego zasobu sieciowego.
- 55/141 -
3.3.5. Możliwości zastosowania języków skryptowych
W rozdziale osiemnastym rekomendacji opisano reguły pisania skryptów oraz
zestawiono atrybuty dostępnych zdarzeń. Domyślnym językiem skryptowym
dla dokumentów SVG jest ECMAScript. Zmiany języka można dokonać
redefiniując atrybut contentScriptType w elemencie głównym <svg>
(domyślną wartością tego atrybutu jest text/ecmascript). Oczywiście
można również określić język skryptowy indywidualnie dla każdego elementu
<script>, ustawiając jego atrybutu type.
Rys.3.12. Rysunek SVG prezentujący wykorzystanie skryptu ECMAScript dotworzenia interaktywnych obrazów (po lewej stronie przedstawiono
oryginalny rysunek, natomiast po prawej obraz zmodyfikowany po kliknięciuprzyciskiem myszy na prostokącie).
Element <script> w SVG odpowiada takiemu samemu elementowi z języka
- 56/141 -
HTML. Każda funkcja zdefiniowana wewnątrz tego elementu ma zasięg
globalny w odniesieniu do całego dokumentu SVG. Przykład wykorzystania
skryptu ECMAScript zamieszczono na rysunku 3.12. Przedstawia on prostokąt,
na który należy kliknąć przyciskiem myszy, aby zmienić jego rozmiar oraz
kolor. Na rysunku zaprezentowano obie fazy działania skryptu: przed oraz po
kliknięciu. Kod źródłowy można zobaczyć na rysunku 3.13. W skrypcie
zdefiniowano funkcję onRectClick, która zostaje wywołana przez zdarzenie
onclick, skojarzone z elementem <rect>. To właśnie w niej dokonują się
modyfikacje atrybutów prostokąta. Dodatkowo przy przesuwaniu myszy nad
prostokątem, wskaźnik zmienia wygląd ze strzałki na dłoń z wysuniętym
palcem wskazującym (ustawienie atrybutu cursor="pointer"), co
sugeruje użytkownikowi możliwość wywołania jakiejś akcji po kliknięciu na
obiekt.
<script type="text/ecmascript"><![CDATA[ function onRectClick( evt ) { var myRect = evt.target; if( myRect.getAttribute("x") == 70 ) { myRect.setAttribute( "x", 30 ); myRect.setAttribute( "width", 340 ); myRect.setAttribute( "y", 150 ); myRect.setAttribute( "height", 250 ); myRect.setAttribute( "fill", "purple" ); } else { myRect.setAttribute( "x", 70 ); myRect.setAttribute( "width", 260 ); myRect.setAttribute( "y", 50 ); myRect.setAttribute( "height", 450 ); myRect.setAttribute( "fill", "green" ); } }]]></script><rect x="70" y="50" width="260" height="450" fill="darkgreen" onclick="onRectClick(evt)" cursor="pointer"/>
Rys.3.13. Kod źródłowy obrazu SVG, przedstawionego na rysunku 3.12.
- 57/141 -
3.3.6. Animacje
Na zakończenie szczegółowej analizy możliwości grafiki SVG przedstawię
najistotniejsze informacje dotyczące animacji, której poświęcony został w
całości dziewiętnasty rozdział rekomendacji W3C. Specyfikacja definiuje
animację w SVG jako dynamiczną zmianę grafiki wektorowej w czasie.
Animację można zrealizować na jeden z trzech poniższych sposobów:
korzystając z przeznaczonych do tego elementów animacji, np.
<animate>
Używając umiejętnie elementów animacji można tworzyć ruchome
ścieżki (ang. motion paths), efekty rozjaśniające i zaciemniające obraz
oraz obiekty, które rosną, kurczą się, obracają lub zmieniają kolor.
korzystając z obiektowego modelu dokumentu SVG DOM, np. w
skryptach
Każdy atrybut oraz każdy styl dokumentu SVG jest dostępny w
skryptach. SVG posiada dodatkowe interfejsy DOM, umożliwiające
efektywne tworzenie animacji za pomocą skryptów. Dzięki nim można
uzyskać praktycznie każdy możliwy rodzaj animacji.
SVG został zaprojektowany w taki sposób, aby umożliwić przyszłym
wersjom języka SMIL (Synchronized Multimedia Integration Language
– język opisu prezentacji multimedialnych korzystający z techniki XML)
wykorzystanie obrazów SVG jako komponentów multimedialnych.
SVG oferuje użytkownikowi cztery elementy animacji opisane w
specyfikacji SMIL: <animate> (modyfikuje wartości atrybutów oraz
właściwości w określonym czasie), <set> (synonim elementu
<animate>, wygodny w sytuacjach, gdy opisujemy animację poprzez
zmianę wartości atrybutów nienumerycznych), <animateMotion>
(przesuwa element wzdłuż ruchomej ścieżki), <animateColor>
- 58/141 -
(modyfikuje kolor wybranego atrybutu w określonym czasie).
Dodatkowo w standardzie zdefiniowano kilka innych elementów,
stanowiących rozszerzenie w stosunku do animacji SMIL.
Na rysunku 3.14 przedstawiono prostą animację. W czasie kilku sekund żółte
koło przesuwane jest od lewej do prawej części ekranu. Ten sam efekt
animacji można uzyskać na kilka sposobów. Na rysunku 3.15 przedstawiono
kod źródłowy zbudowany w oparciu o obiektowy model dokumentu oraz
wykorzystanie języka skryptowego ECMAScript. Identyczny efekt daje
zastosowanie standardowych elementów animacji języka SVG (w tym
przypadku elementu <animate>), którego kod źródłowy można zobaczyć na
rysunku 3.16. W pierwszym przypadku kod jest dużo bardziej rozbudowany,
jednak z drugiej strony daje nam lepszą kontrolę i większą swobodę
manipulacji dokumentem SVG. Decyzja o wyborze metody animacji zależy od
konkretnego przypadku. Wydaje się jednak, że do wykonania prostych
animacji wystarczy zastosowanie podstawowych elementów (łatwiej wtedy
zrozumieć całość dokumentu), z kolei przy bardziej rozbudowanych
projektach skrypty dają większe możliwości napisania uporządkowanego i
przejrzystego kodu.
Rys.3.14. Rysunek przedstawiający prostą animację zrealizowaną za pomocągrafiki SVG.
- 59/141 -
<svg onload="animate(evt)" xmlns="http://www.w3.org/2000/svg" version="1.1"> <script type="text/ecmascript"><![CDATA[ var circle; var min_x = 0; var max_x = 500; var x = min_x; var speed = .1; function animate( evt ) { if( window.svgDocument == null ) svgDocument = evt.target.ownerDocument; circle = svgDocument.getElementById( "myCircle" ); setTimeout( "advance()", speed ); } function advance() { ++x; if( ++x > max_x ) x = min_x; circle.setAttributeNS( null, "cx", x - 50 ); setTimeout( "advance()", speed ); } ]]></script> <circle id="myCircle" cx="-100" cy="120" r="50" stroke="black" stroke-width="5" fill="yellow"/></svg>
Rys.3.15. Kod źródłowy animacji SVG, przedstawionej na rysunku 3.14,zrealizowany w oparciu o obiektowy model dokumentu DOM oraz skrypt
ECMAScript.
<circle id="myCircle" cx="-100" cy="120" r="50" stroke="black" stroke-width="5" fill="yellow"> <animate attributeName="cx" from="-50" to="450" dur="6s" repeatCount="indefinite"/></circle>
Rys.3.16. Kod źródłowy animacji SVG, przedstawionej na rysunku 3.14,zrealizowany w oparciu o standardowy element animacji <animate>.
3.4. Podsumowanie
Trzeci rozdział pracy dyplomowej w całości zdecydowałem się poświęcić
starannej analizie grafiki SVG. Wierzę, że skutecznie przedstawiłem w nim
- 60/141 -
wszystkie najważniejsze informacje, pozwalające czytelnikowi niniejszego
opracowania na wystarczająco dogłębne przyjrzenie się możliwościom
dyskutowanego standardu.
W treści pracy zawarte zostały zarówno podstawowe informacje na temat grafiki
SVG, jak i zagadnienia bardziej szczegółowe. Większość omawianych
właściwości starałem się poprzeć praktycznymi przykładami, prezentując
zarówno gotowy rysunek tak jak wygląda on w oknie przeglądarki internetowej,
jak i kompletny kod źródłowy, który posłużył do jego wygenerowania.
Wiedzę teoretyczną pozyskaną z lektury rekomendacji W3C uzupełniłem w tym
rozdziale umiejętnościami jej praktycznego wykorzystania oraz własną
pomysłowością, pozwalającą na stworzenie czytelnych i przejrzystych
przykładów, które mam nadzieję dały klarowny obraz najważniejszych
możliwości oferowanych twórcom grafiki przez standard SVG.
Na zakończenie niniejszego rozdziału chciałbym pokrótce nawiązać do faktu, iż
SVG jest aplikacją XML i zasygnalizować wiążące się z tym konsekwencje, by
wreszcie w kilku krótkich zdaniach przedstawić widoki rozwoju standardu w
przyszłości, kierunki proponowanych zmian oraz dyskutowane na bieżąco przez
SVG Working Group pomysły na implementację nowych funkcjonalności.
3.4.1. SVG a XML
Bardzo ważnym elementem, na który chciałbym zwrócić uwagę na
zakończenie tego rozdziału jest wyraźne podkreślenie faktu, że SVG jest
aplikacją XML. Ma to swoje bardzo pozytywne konsekwencje. Umożliwia nam
między innymi zagnieżdżanie dokumentów SVG w obrębie innych przestrzeni
nazw XML i vice versa: plik SVG również może zawierać w sobie dokumenty
przygotowane w innych językach XML.
Dokumenty SVG są zwykłymi plikami tekstowymi, a co za tym idzie mogą być
edytowane bezpośrednio w najprostszym edytorze tekstu. Oczywiście nic nie
- 61/141 -
stoi na przeszkodzie, aby tworzyć skomplikowane dokumenty SVG właśnie w
ten sposób. Na szczęście istnieją znacznie wygodniejsze sposoby ich
generowania, między innymi specjalizowane edytory graficzne dedykowane
grafice wektorowej i standardowi SVG.
Pomimo możliwości wykorzystania zaawansowanych aplikacji graficznych do
edycji grafiki SVG, bardzo często może okazać się, iż w przypadku prostych
zadań ich zastosowanie będzie wytaczaniem armat na muchy. Standard XML
daje nam tę fantastyczną możliwość niezwykle szybkiego i efektywnego
tworzenia dokumentów, iż grzechem byłoby z niej nie skorzystać, gdy wydaje
się nam to najodpowiedniejszym rozwiązaniem. Większość zadań
praktycznych, opisanych w niniejszej pracy, wykonałem bez wykorzystania
żadnego specjalistycznego oprogramowania, najczęściej natomiast przy
użyciu prostego edytora tekstu, którego największym atutem było
kolorowanie składni XML. To mocny dowód na to, że edycja dokumentów SVG
jest niezwykle prosta, bardzo często intuicyjna i najczęściej nie wymagająca
dużych nakładów pracy (oczywiście w przypadku zaawansowanych projektów
poziom skomplikowania struktury dokumentu SVG wzrasta, jednak nawet
wtedy edycja pliku w zwykłym edytorze tekstu jest nadal możliwa, głównie
dzięki jasnej i przejrzystej składni samego języka).
Z mojego punktu widzenia jako programisty niezwykle cenną i chyba
najważniejszą konsekwencją faktu, że pliki SVG są zwykłymi plikami
tekstowymi, jest to, że ich przetwarzanie jest niezwykle szybkie i wygodne.
Dostęp do danych składowanych w pliku tekstowym nie wymaga
zastosowania żadnych wyspecjalizowanych interfejsów i jest tak samo łatwy
do zrealizowania w dowolnym języku programowania.
Biorąc pod uwagę ogromną popularność oraz rozpowszechnienie standardu
XML (nie tylko w sieci Internet), istnieje możliwość wykorzystania jednego z
wielu narzędzi wspomagających przetwarzanie dokumentów opracowanych
- 62/141 -
zgodnie z regułami tworzenia języków XML, a więc również SVG.
Bezcenny dla programistów jest również bezpośredni dostęp do kodu
źródłowego każdego stworzonego dokumentu SVG – analiza gotowych
przykładów zawsze stanowić będzie najlepsze źródło pozyskiwania wiedzy w
czasie nauki programowania. Tutaj jest ona na wyciągnięcie ręki (chociaż
może się to zmienić od wersji 2.0 standardu: SVG Working Group analizuje
możliwości ukrywania kodu źródłowego SVG przed przeglądaniem). W
przypadku języków kompilowanych takiej możliwości w zasadzie nie mamy.
3.4.2. Kierunki rozwoju SVG: wersje 1.2 i 2.0
Chociaż standard SVG w wersji 1.1 zdążył już na dobre zadomowić się w
zbiorowej świadomości użytkowników sieci Internet, zarówno członkowie SVG
Working Group jak i indywidualni użytkownicy wciąż dostrzegają pewne jego
niedociągnięcia. Eliminację tych niedostatków ma zapewnić wersja 1.2
standardu, nad którą prace trwają już od kilku lat i która ma obecnie status
wydania roboczego (ang. working draft).
Standard SVG w wersji 2.0 nie posiada jeszcze statusu gotowego dokumentu,
niemniej jednak nieustannie gromadzone są dla niego nowe wymagania [16],
ponieważ ciągle istnieje zapotrzebowanie na nowe funkcjonalności, których
implementacja nie została zaplanowana dla wcześniejszych wersji.
Pomijając tak oczywiste kwestie jak zachowanie kompatybilności wstecz
kolejnych wersji standardu, chciałbym wymienić poniżej kilka z ciekawszych,
spośród wielu proponowanych w kolejnych wersjach, udoskonaleń. Niektóre z
nich opatrzyłem przymiotnikiem „możliwe”, co oznacza, że ich wprowadzenie
nie jest na obecnym etapie projektowania standardu wymagane, jakkolwiek
bardzo poważnie rozważane. Wprowadzenie pozostałych wymienionych
funkcjonalności jest pożądane i konieczne.
Jeżeli chodzi o cechy (ang. feature) typowo graficzne proponowane są między
- 63/141 -
innymi następujące ulepszenia (w nawiasie podaję wersję standardu, w której
proponowane udoskonalenia miałyby się znaleźć):
Możliwe rozszerzenie zbioru predefiniowanych podstawowych kształtów
(o np. łuk, spiralę, gwiazdę, wielokąty foremne) oraz dodanie nowych
atrybutów do już istniejących kształtów (np. kąt obrotu elipsy) (SVG 2.0)
Możliwe definiowanie punktów, a następnie odwoływanie się do nich w
trakcie definiowania figur za pomocą kształtów lub ścieżek (SVG 2.0)
Możliwe utworzenie zbioru predefiniowanych pól kontrolnych interfejsu
użytkownika (ang. user interface controls), które mogą być użyte
podczas interakcji ze stroną WWW, takich jak przyciski (ang. buttons)
czy pola tekstowe (ang. text fields) (SVG 1.2)
Możliwe wyjustowanie tekstu (ang. text justification) w ramach
określonego kształtu (SVG 2.0)
Dostarczenie parametru pozwalającego na określenie sposobu obsługi
białych znaków (ang. whitespaces) (SVG 2.0)
Utworzenie nowego elementu definiującego kolor, do którego można
odwoływać się dokładnie w taki sam sposób, w jaki obecnie odwołuje
się do gradientów oraz wzorów (SVG 1.2)
Możliwe utworzenie nowych typów gradientów, np. stożkowy (ang.
conical), prostokątny (ang. rectangular), cieniowanie Gourauda (ang.
Gouraud shading) i inne (SVG 2.0)
Możliwe utworzenie mechanizmu umożliwiającego określanie kolejności
renderowania elementów, np. przez zastosowanie atrybutu z-index
(SVG 2.0)
Możliwe wprowadzenie koncepcji warstw (ang. layers), ułatwiające
grupowanie elementów (SVG 2.0)
- 64/141 -
Wśród pozostałych cech standardu możemy odnaleźć propozycje między
innymi następujących udoskonaleń:
Zaznaczanie oraz wybieranie nie tylko elementów tekstowych, ale
również elementów graficznych (SVG 2.0)
Opracowanie mechanizmów umożliwiających przesyłanie strumieniowe
(ang. streaming) animacji (SVG 2.0)
Wyzwalanie dynamicznej zawartości na podstawie poziomu
powiększenia (ang. zoom level) oraz pozycji podglądu (ang. location of
the viewport) (SVG 2.0)
Możliwe zaimplementowanie mechanizmów umożliwiających obsługę
dokumentów zawierających dodatkową informację o poziomie
szczegółowości (ang. level of detail), takich jak np. mapy (SVG 2.0)
Możliwe zaimplementowanie mechanizmów zapewniających ochronę
kodu źródłowego, uniemożliwiających dostęp do treści dokumentu (SVG
2.0)
Ważny wniosek, jaki płynie z analizy tego częściowego przeglądu
proponowanych przez SVG Working Group zmian dla kolejnych wersji, jest
taki, że standard SVG nie wymaga obecnie żadnych dramatycznych zmian.
Jego koncepcja została bardzo starannie przemyślana i trudno znaleźć w jego
specyfikacji znaczące uchybienia. Poza pewnymi dodatkami, które ułatwią
tworzenie grafiki, nie jest potrzebna żadna zasadnicza ingerencja w obecny
kształt standardu.
Koncepcyjnie standard SVG praktycznie już teraz osiągnął status języka
kompletnego, chciałoby się rzec gotowego. Opracowano go bardzo starannie i
chociaż, jak każdy projekt, nie jest on pozbawiony pewnych wad i
niedociągnięć, to jest on na pewno standardem bardzo dobrym. Z kolei o
tym, że nie jest to standard martwy, świadczą ciągłe propozycje jego
- 65/141 -
udoskonalania.
Kolejne wersje standardu SVG znajdują się obecnie w fazie projektowej. Minie
zapewne jeszcze kilka lat (a na pewno co najmniej kilka miesięcy) zanim ujrzą
one światło dzienne. Spowodowane jest to przede wszystkim faktem, iż
proponowane poprawki nie wnoszą do całości projektu rewolucyjnych zmian,
a zatem nie ma istotnych nacisków na ich niezwłoczną publikację.
Jestem przekonany, że SVG funkcjonować będzie w obecnym kształcie jeszcze
przez wiele lat. Jednocześnie żywię nadzieję, iż zyska on z czasem coraz
większą popularność, na którą z całą pewnością zasługuje.
- 66/141 -
4. Praktyczna realizacja przykładowych aplikacji
W czwartym rozdziale pracy dyplomowej chciałbym przedstawić kilka praktycznych
przykładów wykorzystania grafiki SVG. W poprzedzających rozdziałach zestawiłem
podstawową wiedzę teoretyczną niezbędną do zrozumienia istoty funkcjonowania
grafiki SVG. Poszerzyłem ją o swoje własne wnioski oraz analizy dotyczące
możliwości jej wykorzystania. W tym miejscu chciałbym zaprezentować płaszczyznę
stanowiącą pomost pomiędzy teorią oraz praktyką. W dalszej części rozdziału
zamierzam przedstawić kilka konkretnych realizacji aplikacji, które stanowią
praktyczną część mojej pracy i mają za zadanie prezentację wybranych
funkcjonalności analizowanego standardu poprzez konkretne przykłady ich
zastosowania.
Opisywane aplikacje zostały zrealizowane przy pomocy różnych technik, tak aby
nie ograniczać się tylko i wyłącznie do wybranych i ulubionych metod
programowania. Ma to na celu pokazanie, że z grafiką SVG można wygodnie
pracować na wielu platformach – część języków programowania oferuje
bezpośrednie wsparcie dla grafiki wektorowej, inne doskonale radzą sobie z
przetwarzaniem danych tekstowych. Twórca ma tutaj ogromną swobodę w
wyborze sposobu dostępu do danych graficznych. Korzystając z możliwości grafiki
SVG, sam może wybrać metodę tworzenia rysunków lub np. zdecydować, czy ich
zawartość powinna być statyczna czy też generowana dynamicznie.
4.1. Generator wykresów statystycznych
W kolejnych sekcjach pracy dyplomowej prezentuję szczegółowo najistotniejsze
elementy oraz przykładowe zastosowania swojej pierwszej praktycznej realizacji
aplikacji, wykorzystującej możliwości grafiki SVG, którą jest prosty generator
- 67/141 -
wykresów statystycznych przygotowany w języku Perl. Najpierw staram się
dokonać krótkiej analizy przydatności wykresów w środowisku WWW, następnie
odnaleźć miejsce dla obrazów SVG w tej dziedzinie, by w końcu omówić
najistotniejsze korzyści, jakie płyną z ich zastosowania. W dalszej części
dokonuję technicznej analizy zrealizowanej aplikacji oraz prezentuję przykłady
jej wykorzystania.
4.1.1. Zastosowanie aplikacji
Wykresy statystyczne spotykamy w Internecie niemal na każdym kroku. Za
ich pomocą prezentowane są wyniki przeróżnych głosowań internetowych,
zestawień porównawczych i wielu innych. Aplikacje generujące wykresy mogą
znaleźć zastosowanie wszędzie tam, gdzie zachodzi potrzeba wizualizacji
porównania ze sobą kilku wielkości, która pozwoli na graficzne urozmaicenie
„suchego” opisu wyników.
Obecnie wykresy na stronach WWW nie są zamieszczane w formatach
wektorowych, najczęściej wykorzystywane są formaty rastrowe, takie jak JPG
czy PNG. Dyskusję o tym, która z tych metod jest lepsza, podejmuję w
kolejnych rozdziałach.
Do generacji wykresów w formacie SVG zdecydowałem się wykorzystać język
Perl, który świetnie nadaje się do przetwarzania tekstów. Pamiętamy, że SVG
jest aplikacją XML, co oznacza, że dokumenty w tym języku mogą być
tworzone za pomocą dowolnego edytora tekstu. Nic nie stoi na przeszkodzie,
aby ułatwić sobie zadanie i skorzystać z możliwości, jakie oferuje skryptowy
język programowania Perl.
4.1.2. Struktura dokumentu SVG
Przed przygotowaniem kodu źródłowego generatora wykresów, musiałem
- 68/141 -
opracować strukturę dokumentu SVG, który miał posłużyć jako szablon
każdego tworzonego rysunku. W tym celu należało się zastanowić, w jaki
sposób najlepiej będzie odzwierciedlić dane rysunku w tekstowej strukturze
dokumentu XML-owego (przykładowe kody źródłowe już wygenerowanych
gotowych rysunków można przeglądać w rozdziałach 6.1.2 oraz 6.1.3,
bezpośrednie spojrzenie na ich budowę ułatwi zrozumienie opisu, który
kontynuuję w kilku kolejnych paragrafach).
W pierwszej kolejności określiłem wartości atrybutów, jakie powinien posiadać
korzeń dokumentu, tj. element <svg>. Najważniejszy atrybut to viewBox,
który pozwala na dynamiczne skalowanie obrazu po stronie klienta, w
zależności od rozmiarów okna jego przeglądarki WWW. Zdecydowałem się na
wykorzystanie tego atrybutu, ponieważ chciałem, aby rysunek wykresu mógł
być każdorazowo dynamicznie przeskalowany i w całości wypełnić okno
przeglądarki.
Następnie za pomocą dwóch elementów <path> definiowana jest ramka
wokół obrazu oraz kolor tła (atrybut fill). Warto zauważyć, że współrzędne
punktów końcowych ścieżki odpowiadają wymiarom rysunku zdefiniowanym
w elemencie <svg>. Te dane pozostają niezmienne dla każdego
generowanego rysunku. Pierwszym elementem, który je różni, jest tytuł
wykresu. Określam go w kolejnym elemencie SVG <text>.
W kolejnym kroku definiowane są dwa gradienty liniowe, które posłużą jako
wypełnienie poszczególnych elementów wykresu. Kolejnym fragmentem
danych jest definicja poziomej i pionowej osi wykresu oraz punktów
wyznaczających na niej kolejne wartości.
Ostatnią najważniejszą część grafiki stanowią kolejne elementy przedstawiane
na wykresie. Każdy element składa się z napisu obróconego o kąt 50 stopni
(transformacja <g transform="rotate(-50)">), oznaczającego nazwę
tego elementu, tekstu prezentującego osiąganą wartość oraz w końcu sam
- 69/141 -
słupek wykresu. Efektowny wygląd słupka uzyskuje się dzięki wykorzystaniu
zdefiniowanego wcześniej w dokumencie kolorowego gradientu i
zastosowaniu go jako wypełnienie prostokąta ograniczającego słupek.
Powyższy krótki opis obejmuje projekt struktury dokumentu SVG. Bardzo
ważne jest, aby stanowił on kompletny model tego, co chcemy ostatecznie
uzyskać za pomocą generatora, ponieważ może okazać się, że ten dokument
będzie później przetwarzany przez inną aplikację (np. arkusz stylu XSLT).
W momencie gdy określono już, jaką strukturę powinien posiadać rysunek
SVG, można zastanowić się nad sposobami jego wygenerowania.
4.1.3. Moduł Perla do generowania wykresów
Za bardzo dobry sposób zbudowania podstaw swojej aplikacji uznałem
opracowanie niezależnego modułu w języku Perl, który spełniać będzie
wszystkie funkcje niezbędne do prostego oraz szybkiego generowania
wykresów. W tym miejscu muszę zaznaczyć dość istotny szczegół.
Mianowicie, zadaniem stworzonej aplikacji jest generacja grafiki na podstawie
wprowadzonych danych, nie jest natomiast żadną jej funkcją weryfikacja
poprawności tych danych. Logika generatora wykresów obejmuje wszystkie
funkcje związane z przetworzeniem wejściowych danych statystycznych w
obraz SVG, to co znajdzie się na tym obrazie już jej nie „interesuje”.
W związku z powyższym, jeśli użytkownik potrzebuje przykładowo
wygenerować wykres udziału procentowego głosów, jakie uzyskały partie
polityczne w wyborach powszechnych, to podając jako dane wejściowe dla
skryptu kolejne wyniki dające w sumie wartość większą niż 100%, nie
spowoduje on błędu działania skryptu. Moduł generatora wykresów wykona
takie przeskalowanie osi, aby odpowiadało ono wprowadzonym danym.
Weryfikacja poprawności tych danych musi odbywać się na wcześniejszym
etapie, ponieważ do obowiązków generatora nie należy analiza treści rysunku,
- 70/141 -
lecz jego bezbłędna generacja. Użytkownik sam musi zapewnić załadowanie
prawidłowych danych do programu.
Moduł w Perlu pełni podobną funkcję, jaką pełni klasa w obiektowym języku
programowania, takim jak na przykład C++. Również tutaj mamy do
czynienia z enkapsulacją. Oznacza to, że użytkownik nie ingeruje
bezpośrednio w kod modułu, lecz korzysta z dostarczonych w nim i dobrze
udokumentowanych funkcji (w C++ metod). Swój moduł nazwałem
„ChartSVG”, aby jego nazwa jak najwierniej odzwierciedlała to, do czego służy
(generowanie wykresów w formacie SVG).
Do dyspozycji użytkownika zdecydowałem się udostępnić następujące
funkcje:
new – konstruktor nowego obiektu typu ChartSVG
set_y_desc – określenie nazwy oraz jednostki osi Y
add – dodanie nowego elementu wykresu
set_title – określenie tytułu całego wykresu
entries – sprawdzenie, ile elementów zostało wstawionych do
wykresu
show_all – wypisanie (na konsolę) listy elementów wstawionych do
wykresu
make_image – wygenerowanie dokumentu SVG na podstawie danych
przekazanych do egzemplarza obiektu
Pełny kod źródłowy modułu można odnaleźć w załączonym na końcu pracy
rozdziale 6.1.1. Na szczególną uwagę zasługuje tutaj funkcja make_image
(pozostałe funkcje nie wymagają wyczerpującego komentarza, gdyż nie
wykonują żadnych skomplikowanych operacji), która wykonuje całą pracę
generacji wykresu na podstawie danych, wstawionych do egzemplarza
- 71/141 -
obiektu modułu ChartSVG. Warto zauważyć, że definicja tej funkcji
pozostawia „furtkę” do generowania zupełnie nowych typów wykresów,
chociaż w prezentowanej tu wersji zaimplementowano tylko jeden (wykres
słupkowy). Wywołanie funkcji make_image powoduje zapisanie na dysku
twardym komputera dokumentu w formacie SVG, zawierającego treść
rysunku, zgodną z danymi dostarczonymi do egzemplarza obiektu modułu.
W module ChartSVG określono dość znaczącą liczbę stałych, dzięki czemu
możliwe są jego szybkie modyfikacje dla własnych potrzeb. Sam moduł
korzysta również z kilku zmiennych, z których największe znaczenie ma
zmienna MAX_ENTRIES_ON_DIAGRAM, która określa maksymalną liczbę
słupków, które zostaną narysowane na wykresie. Jeżeli przykładowo
ustawimy tą zmienną na wartość 5, a funkcję add wywołamy trzykrotnie,
wówczas na wykresie otrzymamy trzy słupki. Ale jeśli funkcja add zostanie
wywołana 10 razy, a MAX_ENTRIES_ON_DIAGRAM jest równe 5, to
narysowane zostanie tylko pięć słupków. Zmienna ta pozwala ograniczyć
maksymalną dozwoloną liczbę słupków prezentowanych na wykresie.
Sam moduł zawiera około 250 linii kodu, czym udowadnia, że generowanie
grafiki SVG z poziomu Perla jest nie tylko bardzo proste, ale i bardzo szybkie.
Efekty działania modułu można oglądać na rysunkach 4.1 oraz 4.3.
W tym miejscu chciałbym zaprezentować dwa konkretne przykłady
wykorzystania przygotowanego wcześniej modułu. Jako pierwszy
przedstawiam kompletny kod, służący do wygenerowania wykresu
przedstawiającego porównanie liczby ludności w najludniejszych państwach
świata. Pełny kod źródłowy można przeglądać w dołączonym na końcu pracy
rozdziale 6.1.2, a jego skróconą wersję zamieściłem na rysunku 4.2. Z kolei
efekt działania tego kodu można zobaczyć na rysunku 4.1. Jak widać na tym
przykładzie, większą część kodu źródłowego stanowi wprowadzanie danych
do egzemplarza obiektu modułu ChartSVG. Dzięki temu, że przygotowany
- 72/141 -
został przeze mnie ten właśnie moduł, generacja gotowego wykresu w
formacie SVG wymaga napisania zaledwie kilku linijek kodu w języku Perl.
Rys.4.1. Rysunek SVG zawierający wykres prezentujący liczbę ludności wnajludniejszych krajach świata, wygenerowany za pomocą modułu ChartSVG
(podgląd wykonany w przeglądarce „Opera”).
Warto zauważyć, że kolejność wprowadzanych danych nie ma większego
znaczenia, ponieważ w trakcie generowania wykresu zostają one
posortowane w kolejności malejącej (służy do tego wewnętrzna funkcja
modułu ChartSVG: sort_all), dzięki czemu użytkownik nie musi się
martwić, jeśli wprowadzane przez niego dane są nieuporządkowane.
W sposób analogiczny do poprzedniego przykładu generowany jest wykres
przedstawiający fikcyjne wyniki wyborów parlamentarnych w 2006 roku. Kod
źródłowy skryptu generującego plik SVG różni się tylko wartościami
przekazywanymi do kolejnych funkcji modułu ChartSVG. Pełny kod źródłowy
można przeglądać w dołączonym na końcu pracy rozdziale 6.1.3, a jego
- 73/141 -
skróconą wersję zamieściłem na rysunku 4.4. Wynik działania tego skryptu
został przedstawiony na rysunku 4.3.
#!/usr/bin/perl
use ChartSVG;
$myChart = ChartSVG->new();$myChart->set_title( "LUDNOŚĆ PAŃSTW ŚWIATA" );$myChart->set_y_desc( "mln" );$myChart->add( "Chiny", 1306.3 );...$myChart->add( "Japonia", 127.4 );
$myChart->make_image( "kraje.svg", "BAR" );
Rys.4.2. Skrócony kod źródłowy programu służącego do wygenerowaniawykresu przedstawiającego porównanie liczby ludności w najludniejszychpaństwach świata w oparciu o moduł Perla „ChartSVG” (pominięto kilka
podobnych wierszy, wstawiających do egzemplarza obiektu klasy ChartSVGdane o kolejnych państwach; pełny kod źródłowy skryptu w rozdziale 6.1.2).
Rys.4.3. Rysunek SVG zawierający wykres prezentujący fikcyjne wynikiwyborów parlamentarnych, wygenerowany za pomocą modułu ChartSVG.
- 74/141 -
#!/usr/bin/perl
use ChartSVG;
$myChart = ChartSVG->new();$myChart->set_title( "WYBORY PARLAMENTARNE 2006" );$myChart->set_y_desc( "%" );$myChart->add( "SLD", "7.86" );...$myChart->add( "PD", 2.35 );
$myChart->make_image( "wybory.svg", "BAR" );
Rys.4.4. Skrócony kod źródłowy programu służącego do wygenerowaniawykresu przedstawiającego fikcyjne wyniki wyborów parlamentarnych woparciu o moduł Perla „ChartSVG” (pominięto kilka podobnych wierszy,
wstawiających do egzemplarza obiektu klasy ChartSVG dane o kolejnychpartiach politycznych; pełny kod źródłowy skryptu w rozdziale 6.1.3).
Jak widać opracowanie specjalnego modułu, służącego do generowania
wykresów w formacie SVG, pozwoliło dramatycznie uprościć proces generacji
wykresów. Jestem przekonany, że nawet laik nie mający pojęcia o
programowaniu w języku Perl (chociaż oczywiście mający ogólne pojęcie o
programowaniu), potrafiłby w pełni skutecznie skorzystać z możliwości
oferowanych przez przygotowany przeze mnie moduł.
4.1.4. Czy SVG jest koniecznością?
Dlaczego upierać się przy stosowaniu grafiki SVG do generowania wykresów,
zamiast pozostać przy zupełnie poprawnie funkcjonujących formatach JPG czy
PNG, zwłaszcza, że wygenerowanie grafiki SVG po stronie klienta zużywa
znacznie więcej zasobów niż odtworzenie danych z pliku w formacie JPG?
Chodzi tu przede wszystkim o rozmiar przesyłanych przez sieć informacji.
Pamiętajmy, że to nie zasoby sprzętowe stanowią obecnie problem.
Problemem w sieci jest wciąż tak zwane „wąskie gardło”, którym jest
przyłącze klienta końcowego do sieci Internet. Duża szybkość renderowania
obrazów rastrowych po stronie klienta niekoniecznie musi rekompensować ich
- 75/141 -
powolny transfer z sieci. W trakcie korzystania z Internetu wielokrotnie
doświadczam sytuacji, w której obserwuję jak obraz w formacie JPG bardzo
powoli wyświetla się na ekranie mojego monitora. Dane przesyłane są powoli,
ponieważ jest ich bardzo dużo – w zależności od stopnia kompresji oraz
rozmiaru obrazu może to być od kilkudziesięciu kilobajtów do nawet kilku
megabajtów informacji! Tymczasem grafika SVG pozwala wielokrotnie
zmniejszyć wielkość pliku przechowującego analogiczne informacje.
Przy obecnych możliwościach zwykłych komputerów osobistych, renderowanie
obrazu SVG zajmuje ułamek sekundy. Ponieważ dane takiego dokumentu
stanowią wyłącznie tekst, dlatego dają się świetnie skompresować, a co za
tym idzie przesyłać przez sieć znacznie szybciej niż pliki z grafiką rastrową.
Jest to silny argument za tym, aby pewne specyficzne obrazy (takie jak
wykresy) były publikowane na stronach WWW w formacie SVG. Myślę, że
bardzo przekonujące jest to, że czas pobierania strony WWW zawierającej
grafikę SVG będzie znacznie krótszy niż tej, która zawiera identyczne obrazy
w formacie JPG.
Odpowiadając na pytanie postawione w tytule niniejszego podrozdziału
muszę powiedzieć, że oczywiście nie jest żadną koniecznością publikowanie
wykresów na stronach WWW w formacie SVG. Natomiast niewątpliwie będzie
to stanowić wielką ulgę dla wszystkich tych użytkowników końcowych, którzy
nie posiadają bardzo szybkiego łącza dostępowego do sieci Internet.
Zadowolenie użytkownika z szybkiego załadowania strony WWW może się z
całą pewnością opłacić jej twórcy, zwłaszcza, że punktu widzenia tego
użytkownika mniej istotne jest to, jakie techniki zostały użyte do
przygotowania witryny, a bardziej to, jak długo musi on oczekiwać na jej
załadowanie do swojej przeglądarki internetowej.
W tabeli 4.1 zestawiłem porównanie rozmiarów pliku, zawierających dane
graficzne wykresu wygenerowanego za pomocą opracowanego przeze mnie
- 76/141 -
modułu ChartSVG, w różnych formatach graficznych (skorzystałem z
rysunków 4.1 oraz 4.3 o rozmiarze 1024x768 pikseli).
Tabela 4.1. Porównanie rozmiarów plików graficznych popularnych formatów,zawierających dane identyczne z rysunkami SVG, wygenerowanymi za
pomocą stworzonego przeze mnie generatora wykresów – skorzystano zrysunków 4.1 oraz 4.3 o rozmiarach 1024x768 pikseli (rozmiary w bajtach).
nazwa pliku SVGZ SVG JPG PNG GIF BMP
wybory.svg 1 145 6 042 64 328 89 192 192 627 2 359 350
kraje.svg 1 205 6 457 72 390 118 390 114 174 2 359 350
Tabela 4.1 daje nam zdumiewający przykład na to, jak niewielki rozmiar mogą
mieć rysunki SVG! Warto w tym miejscu przypomnieć, że rozmiary plików w
pozostałych formatach będą zmieniać się wraz ze skalowaniem obrazu
(równocześnie pogarszać się będzie ich jakość), podczas gdy rysunek SVG
zawsze będzie miał ten sam rozmiar (najwyższa jakość grafiki pozostanie bez
zmian).
4.1.5. Wykres jako skrypt CGI
Skrypty CGI są używane do dynamicznego generowania zawartości stron
WWW. Tworzone są one w różnych językach programowania, niemniej jednak
jednym z najpopularniejszych sposobów ich tworzenia, jest pisanie skryptów
w języku Perl (utarło się wśród programistów przekonanie, że jeśli mówimy o
skryptach CGI, to niemal na pewno mówimy równocześnie o języku Perl).
Bardzo łatwo można przebudować zaprezentowany przeze mnie w
poprzedniej sekcji kod generujący wykres w taki sposób, aby mógł być on
uruchamiany jako skrypt CGI. Dzięki temu w zależności od tego, jakie dane
zostałyby dostarczone do skryptu, każdorazowo moglibyśmy dynamicznie
generować zupełnie nowy wykres z zupełnie nowymi danymi. Kod w Perlu,
- 77/141 -
który miałby funkcjonować jako skrypt CGI, wymagałby minimalnych
modyfikacji, które przedstawiłem na rysunku 4.5.
#!/usr/bin/perl
use CGI;
# $data - do tej zmiennej należy w tym miejscu zapisać# treść dokumentu SVG
print "Content-Type: image/svg+xml\n";print "Content-Length: ", length($data), "\n\n$data";
1;
Rys.4.5. Fragment kodu źródłowego zawierającego modyfikacje, o jakienależałoby rozbudować kod generujący wykresy w oparciu o moduł
ChartSVG, tak aby mógł on funkcjonować jako skrypt CGI.
Jedyne modyfikacje, które należałoby wykonać, to wskazanie przeglądarce, z
jakim typem dokumentu ma ona do czynienia oraz jaki jest jego rozmiar.
Potęga skryptów CGI pisanych w Perlu polega właśnie na tym, że dane, które
program zapisuje bezpośrednio do standardowego wyjścia, po stronie klienta
widziane są jako treść pobieranego pliku.
Tylko od użytkownika skryptu zależałoby, w jaki sposób wykorzystać jego
możliwości. Można by przykładowo stworzyć formularz na stronie WWW, który
przekazuje do skryptu informacje wpisane przez gości strony jako jego dane
wejściowe. To, czy skrypt miałby generować od razu dokument SVG, czy też
wstawiać grafikę SVG w określonym miejscu na stronie WWW, zależałoby już
tylko od preferencji użytkownika lub autora strony.
4.1.6. Możliwości rozbudowy
Zaprezentowany w tym rozdziale napisany przeze mnie moduł Perla, służący
do generacji wykresów, ma stosunkowo ubogie możliwości. Jego
funkcjonalność można by jeszcze rozszerzyć o wiele różnorodnych opcji. Moim
celem nie było jednak przygotowanie rozbudowanej aplikacji generującej
- 78/141 -
wykresy, tylko zasygnalizowanie korzyści, jakie płyną z zastosowaniu grafiki
SVG do tego celu. Utworzenie modułu Perla do tego celu było powodowane
chęcią stworzenia eleganckiej metody prezentującej wszystkie pozytywne
cechy, jakie niesie ze sobą integracja SVG (i w ogóle wszystkich aplikacji XML)
z możliwościami języka Perl.
Co można by jeszcze dodać w module ChartSVG? Na pewno można by
pokusić się o przygotowanie generatorów innych typów wykresów: wykresów
kołowych, liniowych, punktowych czy też ich trójwymiarowych reprezentacji.
Można by pozwolić na wyróżnianie wskazanych elementów. Można by
opracować algorytm pozwalający na wykreślanie linii trendu na wykresie,
prezentację wybranego wycinka wykresu w powiększeniu, a nawet animację
wybranych elementów, pokazujących przykładowo zmiany zachodzące we
wskazanym obszarze w czasie. Te wszystkie pomysły są możliwe do
zrealizowania za pomocą wielu różnorodnych aplikacji, jednak format SVG
pozwala na zaskakującą prostotę zapisu i przede wszystkim doskonałą
kompresję rozmiaru tych danych.
4.2. Generator wykresów pogodowych
Kolejną praktyczną aplikacją, zrealizowaną w ramach mojej pracy dyplomowej,
jest arkusz XSLT do transformacji danych pogodowych, przechowywanych w
zaprojektowanym przeze mnie formacie dokumentu XML, do formatu grafiki
SVG. W kilku kolejnych podrozdziałach staram się przedstawić koncepcję
realizacji tego projektu, uzasadniam decyzję o wyborze technik
programistycznych, przedstawiam bardziej zawiłe elementy tworzenia wykresów
i wreszcie prezentuję kilka przykładowych rysunków wraz z krótką
charakterystyką danych, które posłużyły do ich wygenerowania.
- 79/141 -
4.2.1. Zalety wykorzystania grafiki wektorowej SVG
Najistotniejszą pozytywną cechą, jaką niesie dla omawianej aplikacji grafika
SVG, jest jej skalowalność. Pozwala ona w bardzo elastyczny sposób
rozmieszczać różnorodne wykresy na pojedynczej stronie internetowej,
personalizować jej wygląd pod kątem preferencji poszczególnych
użytkowników, czy też dowolnie zmieniać rozmiary prezentowanych wykresów
w celu analizy wybranych charakterystyk.
Już w drugim rozdziale wspominałem, że realizacja aplikacji wykorzystującej
możliwości grafiki SVG do tworzenia wykresów pogodowych na stronach
WWW była jednym z pierwszych zadań, jakie postawiłem sobie do realizacji w
ramach niniejszej pracy dyplomowej. Ponieważ celem mojej pracy nie jest
realizacja żadnego dużego projektu informatycznego wykorzystującego
standard SVG, lecz analiza różnych obszarów Internetu, w których ten
standard można śmiało wykorzystać, również opisywana tutaj aplikacja nie
jest kompletnym rozwiązaniem systemu do zbierania, przechowywania oraz
prezentowania danych pogodowych. Swój projekt ograniczyłem do tych
elementów, które bezpośrednio dotyczą grafiki, tj. do wizualizacji danych
pogodowych w postaci wykresów w formacie SVG.
Jest oczywistym, że aby móc wykonać graficzną reprezentację pewnych
danych, należy te dane posiadać. Do tego celu musiałem zaprojektować swój
własny model danych XML i zrobić to w taki sposób, aby jak najlepiej
odzwierciadlał rzeczywiste dane, które mogłyby posłużyć jako źródło
informacji do dalszego przetwarzania. Aby przekształcić liczbowe dane do
grafiki w formacie SVG zdecydowałem się tym razem na skorzystanie z
możliwości innego XML-owego narzędzia, mianowicie XSLT (ang. eXtensible
Stylesheet Language Transformations), umożliwiającego przekształcanie
różnych dokumentów zgodnych ze standardem XML do innych formatów. W
tym przypadku potrzebowałem dokonać transformacji z mojej własnej
- 80/141 -
aplikacji XML do grafiki SVG. Efektem mojej pracy było opracowanie arkusza
stylu XSLT, który to zadanie realizował. W następnych podrozdziałach
przedstawiam szczegółowo charakterystykę obu omawianych składników
aplikacji, jak również prezentuję kilka gotowych przykładów, będących
efektem ich wykorzystania.
4.2.2. Format przechowywania danych pogodowych
Do przechowywania danych o pogodzie został zaprojektowany dokument
XML. Ponieważ format SVG sam jest aplikacją XML, uznałem za stosowne
wykorzystanie takiej techniki, która pozwoli zaprezentować wszystkie
najlepszy cechy, jakie niesie ze sobą modelowanie danych za pomocą
metajęzyka XML.
Pierwszym krokiem projektu było opracowanie definicji DTD (ang. Document
Type Definition – definicja typu dokumentu), na której zostałyby oparte
wszelkie możliwe rodzaje danych pogodowych. Zdecydowałem się
zaprojektować tylko jedną definicję DTD, aby później móc korzystać z
jednego tylko arkusza stylu XSLT, który posłużyłby do transformacji danych z
dokumentu XML do formatu SVG. Z jednej strony może się wydawać, że takie
podejście może powodować niepotrzebną komplikację struktury danych, ale w
praktyce rozbudowie ulega w zasadzie tylko arkusz stylu XSLT, który musi
„obsłużyć” większą liczbę możliwych kombinacji danych.
Wiadomo, że inny charakter mają dane takie jak temperatura, a inny opady.
Niemniej jednak będę chciał pokazać, że zaproponowany przeze mnie model
danych pozwala nie tylko na wygodne przechowywanie tych danych w tym
samym typie dokumentu XML. Co więcej, dwa różne rodzaje danych można
swobodnie zaprezentować na jednym wspólnym wykresie, co przedstawię
później na bardzo konkretnym przykładzie.
Należy w tym miejscu zaznaczyć, że definicja DTD nie została przeze mnie
- 81/141 -
zaprojektowana w ciągu jednej chwili i nie ulegała później żadnym zmianom.
Wprost przeciwnie, w trakcie pisania arkusza stylu XSLT byłem zmuszony
wielokrotnie dokonywać zmian w swoim pierwotnym projekcie dokumentu
XML, aby poprawnie odzwierciedlić wszystkie dodatkowe cechy rysunków, na
których realizację decydowałem się w czasie realizowania projektu.
Początkowo wykresy miały zawierać następujące informacje: tytuł, godziny
oraz daty wybranych punktów, ich wartości oraz właściwy diagram (wykres
liniowy) reprezentujący określoną wielkość pogodową. W związku z tym tylko
te elementy znalazły się w pierwotnym projekcie dokumentu XML. Dopiero
później wprowadzałem kolejne składniki, mające na celu zbudowanie bardziej
atrakcyjnych wykresów, tzn. rozróżnienie na typy wykresów, zróżnicowanie
metod prezentowania danych (linie, słupki, itd.), możliwość zastosowania
nietypowych skali na osi rzędnych oraz zamieszczanie różnych typów
wykresów na tym samym diagramie.
Ostatecznie powstał projekt DTD, który stanowił podstawę dla
przechowywania danych pogodowych, które z kolei miały posłużyć do
tworzenia obrazów SVG za pomocą przekształcenia XSLT. Na rysunku 4.6
przedstawiłem kod źródłowy, który należałoby dołączać do każdego
dokumentu XML, zawierającego dane pogodowe, aby pozostał on w zgodzie z
moim projektem.
Jak widzimy korzeń całego dokumentu stanowi element weather i posiada
on kilka obowiązkowych sekcji. Pierwszą sekcją jest description, która
określa tytuł wykresu oraz decyduje o tym, co zostanie wyświetlone w górnej
części obrazu SVG. Sekcja dates zawiera listę godzin oraz dat, które zostaną
przypisane poszczególnym punktom na osi odciętych. Warto w tym miejscu
zwrócić uwagę na to, że już na etapie projektu definicji DTD założyłem sobie,
że liczba dozwolonych punktów nie będzie w żaden sposób normalizowana –
bez względu na liczbę takich pomocniczych pionowych linii, jaką życzyłby
- 82/141 -
sobie zobaczyć na wykresie użytkownik korzystający z tej aplikacji, należy się
postarać, aby na wykresie rzeczywiście została przedstawiona taka ich liczba,
jakiej zażąda użytkownik.
<!DOCTYPE weather [ <!ELEMENT weather (description,dates,values,diagram+)> <!ELEMENT description (title,additional_title?)> <!ELEMENT title (#PCDATA)> <!ELEMENT additional_title (#PCDATA)> <!ELEMENT dates (date+)> <!ELEMENT date EMPTY> <!ELEMENT values (value+)> <!ELEMENT value EMPTY> <!ELEMENT diagram (point+)> <!ELEMENT point EMPTY> <!ATTLIST weather diagram_bckg_color CDATA #REQUIRED> <!ATTLIST title outline_color CDATA #REQUIRED> <!ATTLIST additional_title outline_color CDATA #REQUIRED> <!ATTLIST date day CDATA #REQUIRED> <!ATTLIST date hour CDATA #REQUIRED> <!ATTLIST values left_color_shadow CDATA #IMPLIED> <!ATTLIST values right_color_shadow CDATA #IMPLIED> <!ATTLIST value left CDATA #IMPLIED> <!ATTLIST value right CDATA #IMPLIED> <!ATTLIST diagram type CDATA #REQUIRED> <!ATTLIST diagram name CDATA #REQUIRED> <!ATTLIST diagram style CDATA #REQUIRED> <!ATTLIST diagram color CDATA #REQUIRED> <!ATTLIST diagram values (left|right) #REQUIRED> <!ATTLIST point value CDATA #REQUIRED> <!ATTLIST point min_value CDATA #IMPLIED> <!ATTLIST point max_value CDATA #IMPLIED>]>
Rys.4.6. Projekt definicji DTD, na której zostały oparte wszystkie generowanerodzaje danych pogodowych. W plikach XML bazujących na tej definicji
przechowywane są dane pogodowe, które można później przekształcić doformatu grafiki SVG za pomocą odpowiedniego arkusza stylu XSLT.
Kolejna sekcja values ma za zadanie przypisać wartości kolejnych punktów
na osi rzędnych. Wartości definiowane są jako atrybuty elementu value:
left oraz right. Zdecydowałem się na wprowadzenie takiego zabiegu,
- 83/141 -
ponieważ chciałem, aby na jednym diagramie możliwe było zamieszczenie
dwóch zupełnie różnych wykresów. Przykładowo jeden wykres (dla którego
wartości kolejnych punktów zapisane byłyby po lewej stronie diagramu)
dotyczyłby danych temperaturowych, natomiast drugi (wartości po prawej
stronie) stanowiłby reprezentację wilgotności powietrza w kolejnych
godzinach. Co jest tutaj kolejną wartą odnotowania cechą mojej aplikacji to
to, że oba wykresy niekoniecznie musiałyby posiadać tą samą dokładność.
Przykładowo temperatura mogłaby być mierzona w odstępach 3 godzin,
natomiast wilgotność co godzinę. Arkusz stylu XSLT uwzględnia te
właściwości i za każdym razem odpowiednio skaluje obie osie, tak aby te dwa
zupełnie różne wykresy zostały prawidłowo wykreślone.
Ostatnia obowiązkowa sekcja dokumentu XML to diagram, przy czym musi
ona wystąpić co najmniej jeden raz (ale może też więcej). To tutaj właśnie
powinny zostać zawarte wartości poszczególnych punktów wykresów. Dzięki
temu, że moje DTD pozwala na wprowadzenie kilku takich sekcji, dane
pogodowe dotyczące różnych dziedzin, mogą znaleźć się w tym samym
dokumencie XML, a co za tym idzie, narysowane również na tym samym
wykresie SVG. Takie rozwiązanie pozwala na dużą elastyczność w
podejmowaniu decyzji o tym, co chcemy przedstawić na rysunku. Wybór, czy
rozbić dane na cztery oddzielne, ale bardzo przejrzyste wykresy, czy też
zaprezentować je tylko na jednym rysunku, oszczędzając przy tym cenne
miejsce na swojej stronie internetowej, należy już tylko do projektanta
serwisu.
Moja aplikacja pogodowa pozwala na wybór nie tylko jednego z
wymienionych rozwiązań, ale każde możliwe rozwiązanie, które wyda się
użytkownikowi najkorzystniejsze, włącznie z personalizowaniem ustawień dla
różnych gości jego witryny. Jedynym ograniczeniem, które jest narzucone, to
konieczność zastosowania jednego z istniejących mechanizmów rysowania
- 84/141 -
wykresów (dla różnych typów wykresów, zaprojektowałem różne metody ich
prezentacji, szczegóły zostały przedstawione w dalszej części tekstu),
jakkolwiek nic nie stoi na przeszkodzie, aby rozbudować arkusz stylu XSLT o
zupełnie nowe rodzaje wykresów (jak łatwe jest to zadanie będzie się można
również przekonać w trakcie dalszej lektury pracy).
Projekt dokumentu XML z danymi pogodowymi starałem się ograniczyć do
niezbędnego minimum, aby był on tak przejrzysty jak to tylko możliwe (jeżeli
ktoś chciałby odczytać z niego pogodę, powinien móc tego dokonać w tak
samo wygodny sposób jak analizując rysunek z wykresem). Niemniej jednak
istnieje jeden element, który może szczególnie razić w moim projekcie. Jest
to fakt zawarcia danych dotyczących sposobu prezentacji danych graficznych,
na przykład określania kolorów poszczególnych wykresów, w miejscu, w
którym powinny znaleźć się tylko i wyłącznie dane pogodowe (przecież o to
właśnie chodzi w XML-u: o oddzielenie treści od prezentacji). Zdecydowałem
się na taki krok świadomie. Chodziło mi o to, aby pokazać szybki i skuteczny
sposób na zmianę niektórych preferencji obrazu, bez konieczności
bezpośredniej ingerencji w arkusz stylu XSLT.
Informacji o danych graficznych SVG jest w moim dokumencie XML
stosunkowo dużo i gdyby je usunąć, to definicja DTD skurczyłaby się o około
20%. Jest rzeczą oczywistą, że projektując poważną aplikację, należałoby tak
właśnie postąpić. Ponieważ jednak nie chciałem zaciemniać jeszcze bardziej
arkusza stylu XSLT, który i tak w trakcie realizowania aplikacji rozrósł się do
ponad 200 linii kodu i co za tym idzie nieznacznie skomplikował,
zdecydowałem się na tak drastyczny krok. Ułatwiło mi to późniejsze
wykonanie przykładowych wykresów, które mogły różnić się kolorystyką,
dopasowywaną do swojego charakteru, tylko dzięki edycji danych XML.
- 85/141 -
4.2.3. Proces transformacji danych arkuszem stylu XSLT
Aby przekształcić jeden dokument XML w inny, należy opracować odpowiedni
arkusz stylu XSLT. Celem wykazania, że transformacja z dokumentu opartego
na własnej definicji DTD do formatu grafiki SVG jest możliwa, a co więcej
bardzo łatwa, całą funkcjonalność swojej aplikacji do prezentowania
wykresów pogodowych zdecydowałem się oprzeć właśnie na przekształceniu
XSLT.
W każdym dokumencie XML, zgodnym z definicją DTD przedstawioną w
podrozdziale 4.2.2, można umieścić następującą informację:
<?xml-stylesheet type="text/xsl" href="weather.xsl"?>
Oznacza ona, że z tym dokumentem XML skojarzony został arkusz stylu XSLT,
za pomocą którego dokonane zostanie przekształcenie danych do formatu
SVG.
Plik weather.xsl oczywiście musi być zgodny z XML-em, ponieważ XSLT
jest aplikacją XML. Pomijając komentarze, które zostały zamieszczone
wewnątrz pliku źródłowego, mój dokument XSLT zajmuje mniej niż 200 linii
kodu. Ta uwaga warta jest odnotowania, ponieważ efekt, jaki uzyskujemy po
jego zastosowaniu, to poprawnie skomponowany rysunek zawierający
przejrzyste wykresy pogodowe.
Plik XSLT ma rozmiar 15 kilobajtów. Żaden rysunek rastrowy,
odzwierciedlający z podobną dokładnością tak dużą liczbę szczegółów, nie
byłby w stanie zmieścić się w tak niewielkiej przestrzeni dyskowej, nawet jeśli
do arkusza XSLT doliczymy plik zawierający 8 kilobajtów danych o pogodzie
(plik XML). Po skompresowaniu obu plików do formatu .tgz zajmują one
ledwie 4 kilobajty przestrzeni dyskowej! Tak niewielkie rozmiary plików
otrzymujemy po pierwsze dzięki przeniesieniu całej logiki związanej z
- 86/141 -
renderowaniem grafiki do komputera użytkownika końcowego, a po drugie
dzięki temu, że pliki XML mają format tekstowy, który znakomicie się
kompresuje.
Chciałbym teraz pokrótce omówić sposób, w jaki odbywa się tworzenie
rysunku SVG w oparciu o omówiony w poprzednim podrozdziale dokument
XML. Podczas lektury dalszego tekstu warto spoglądać od czasu do czasu na
kod źródłowy, do którego się w nim odwołuję. Pełny kod źródłowy arkusza
stylu XSLT zamieściłem w rozdziale 6.2.3 na końcu pracy dyplomowej.
Pierwsza linia kodu to standardowa deklaracja dokumentu XML, natomiast to,
co jest kluczowym elementem dla poprawnego przenikania się elementów z
kilku różnych przestrzeni nazw w jednym dokumencie (a tutaj przecież
przenikać się mają elementy XSLT oraz SVG), to ich nazwanie w elemencie
nadrzędnym całego dokumentu. Elementem nadrzędnym dla pliku w formacie
XSLT jest <xsl:stylesheet>. I właśnie w nim zadeklarowałem informację
o tym, że w dokumencie korzystam z dwóch zupełnie różnych przestrzeni
nazw:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:svg="http://www.w3.org/2000/svg">
Do definiowania przestrzeni nazw służy atrybut xmlns. W powyższym
przykładzie widać, że korzystam z dwóch przestrzeni nazw: przedrostkiem
xsl: zdecydowałem się poprzedzać wszystkie elementy standardu XSLT,
natomiast przedrostkiem svg: te, które są elementami SVG.
Możliwość korzystania z wielu różnych przestrzeni nazw w jednym
dokumencie XML jest jedną z najpotężniejszych możliwości, jakie daje nam
ten standard. Dzięki niej bezproblemowo przenikają się i wzajemnie
zagnieżdżają elementy XSLT oraz elementy SVG, a przeglądarka
interpretująca dokument XML za każdym razem wie, z elementem której
- 87/141 -
przestrzeni nazw ma w danej chwili do czynienia.
Kolejnym bardzo istotnym składnikiem projektowanej przeze mnie
transformacji było poprawne określenie typu dokumentu wyjściowego. Miał to
być oczywiście plik w formacie SVG, ale nie mogłem sobie przecież pozwolić
na wstawienie takiej deklaracji DTD:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
Byłoby to niezgodne z prawdą, bo przecież w poprzedniej linii kodu
zadeklarowałem fakt korzystania z dwóch różnych przestrzeni nazw. Zamiast
tego należało skorzystać z jednego z elementów standardu XSLT, mianowicie
<xsl:output>, który służy właśnie do definiowania formatu dokumentu
wyjściowego transformacji:
<xsl:output method="xml" indent="yes" doctype-public="-//W3C//DTD SVG 1.1//EN" doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"/>
Kluczowe dla skutecznego przekształcenia są dwa atrybuty: doctype-
public oraz doctype-system. Pierwszy z nich ustawia wartość atrybutu
PUBLIC wyjściowej deklaracji DOCTYPE, natomiast drugi ustawia jej atrybut
SYSTEM. Taki zapis jest całkowicie tożsamy z zapisaną wprost w dokumencie
SVG deklaracją DOCTYPE, natomiast w dokumencie XSLT chroni on przed
niewłaściwym zinterpretowaniem formatu wyjściowego przez procesor
przekształcający dokument z jednego formatu XML w inny.
Gdy wiadomo już elementy których przestrzeni nazw mogą się pojawić w
kodzie źródłowym przekształcenia oraz poprawnie określony został format
dokumentu wyjściowego transformacji, można przystąpić do analizy samego
arkusza stylu XSLT. Można dowiedzieć się z niego, jakie operacje wykonywane
są w trakcie generowania rysunku zawierającego wykres oparty na danych ze
- 88/141 -
źródłowego dokumentu XML.
Aby podkreślić skalowalność wygenerowanego rysunku, zdecydowałem się
wprowadzić na początku kodu transformacji kilka zmiennych globalnych.
Definiują one całkowity rozmiar rysunku (ponieważ obraz jest skalowalny,
więc lepiej w tym miejscu mówić o jego proporcjach – w praktyce manipuluję
tylko stosunkiem wysokości do szerokości, gdyż rysunek i tak wypełni
maksymalnie dostępny obszar w oknie przeglądarki) oraz położenie i rozmiary
samego wykresu. Dzięki tym zmiennym bardzo łatwo i szybko jesteśmy w
stanie dostosowywać wielkość rysunku do naszych własnych potrzeb. W
kodzie źródłowym przekształcenia ten fragment jest opatrzony komentarzem
„XSLT STYLESHEET VARIABLES”. Jako przykład manipulowania tymi danymi
podam wykorzystanie zmiennej pictureWidth. Zmienna ta definiuje
szerokość samego wykresu. Jeżeli w dokumencie źródłowym XML
zdefiniowano dwa różne rodzaje danych pogodowych z zamiarem
przedstawienia ich na jednym wykresie, wówczas wskazane jest
przedstawienie dwóch różnie opisanych osi wartości (po lewej stronie
wykresu dla jednej i po prawej stronie dla drugiej). Jeżeli później okaże się,
że zależy nam na usunięciu jednej z tych wartości z wykresu, wówczas wolne
miejsce z prawej strony rysunku, które zajmowane było wcześniej przez
wartości liczbowe, pozwala na poszerzenie wykresu za pomocą modyfikacji
wartości zmiennej pictureWidth, a co za tym idzie na zwiększenie jego
czytelności oraz ekonomiczniejszego rozdysponowania pustej przestrzeni
rysunku.
Kolejna sekcja dokumentu XSLT, opatrzona komentarzem „DOCUMENT
MATCHING ROOT ELEMENT”, służy do wygenerowania na wyjściu
transformacji głównego elementu <svg>, a także do wykreślenia obramowań
całego rysunku, jak i samego wykresu oraz do pokolorowania tła na wskazany
w źródłowym dokumencie XML kolor. Wartości atrybutów definiujących
- 89/141 -
rozmiary wykreślanych prostokątów (do tworzenia obramowań
wykorzystywane są elementy <rect> ze zbioru dostępnych elementów SVG)
pobierane są ze zdefiniowanych w poprzedniej sekcji dokumentu zmiennych
globalnych, np. zapis width="{$pictureWidth}" oznacza, że w
momencie przetwarzania dokumentu jako wartość atrybutu width
wykorzystana zostanie wartość przechowywana aktualnie w zmiennej
pictureWidth. Taka konstrukcja arkusza stylu XSLT zwalnia użytkownika z
konieczności szczegółowej analizy kodu źródłowego – chcąc dokonać
znaczących modyfikacji w wyglądzie swojego wykresu, musi on jedynie
dokonać zmian albo w danych (dokument XML), albo kilku globalnych
zmiennych (dokument XSLT). Oczywiście nic nie stoi na przeszkodzie, aby na
przykład te kilka zmiennych globalnych przenieść do źródłowego dokumentu
XML i tam dokonywać ich modyfikacji (jedynym kłopotem mogłaby być
wówczas konieczność modyfikacji przygotowanej wcześniej definicji DTD –
chociaż w praktyce procesor XSLT nie zwraca uwagi na obecność definicji
DTD w dokumencie XML, dobrze jest zachować ją w zgodzie z treścią
dokumentu, ponieważ pozwala to uniknąć niejednoznaczności podczas jego
stosowania w przyszłości).
Sekcja opisana jako „DOCUMENT MATCHING DESCRIPTION ELEMENT” służy
do wyświetlenia tytułu wykresu w postaci tekstu (zastosowany zostaje tu
element <text> ze zbioru elementów SVG). Do tego celu wykorzystywane są
standardowe atrybuty, określające położenie, rozmiar oraz styl napisu. Tekst,
który ma zostać wyświetlony, wstawiony zostaje ze źródłowego dokumentu
XML. Jeżeli zdefiniowany został dodatkowy nagłówek (jest to istotne w
przypadku, gdy na jednym diagramie chcemy przedstawić dwa różne rodzaje
wykresów), to zostanie on wyświetlony na rysunku poniżej głównego tytułu
(zastępując daty, które widoczne są tylko na rysunkach bez dodatkowego
nagłówka).
- 90/141 -
Pierwszą sekcją, która jest szczególnie godna uwagi, jest część kodu
źródłowego arkusza stylu XSLT opatrzona komentarzem „DOCUMENT
MATCHING DATES ELEMENT”. W podrozdziale 4.2.2 zaznaczyłem, że
wewnątrz znacznika <dates> przechowywane są informacje dotyczące
kolejnych punktów na osi X oraz odpowiadające im daty i godziny.
Przetwarzaniu tych informacji poświęcony został stosunkowo rozbudowany
kod źródłowy, który musi wykonać kilka różnych operacji: wyświetlić godzinę
oraz pionową linię odpowiadającą danemu punktowi wykresu, jak również
daty odpowiadające kolejnym dniom przedstawionym na diagramie. O ile w
pierwszym przypadku przedstawienie danych polega na prostej iteracji przez
wszystkie dostępne wartości, o tyle w drugim sytuacja się komplikuje. Widać
to na przykładach zaprezentowanych w rozdziale 4.2.4.
Obliczenie centralnej pozycji (w poziomie) napisu daty odpowiadającej
danemu dniu zależy od tego, ile godzin z tego dnia jest przedstawianych na
wykresie. Ponieważ nie chciałem stosować w swoim kodzie skomplikowanych
wywołań rekurencyjnych, które owszem spełniają swoją rolę, to jednak
sprawiają czasami kłopoty przeglądarkom (pamiętam, że w trakcie tworzenia
dokumentu XSLT bardzo długo nie mogłem znaleźć przyczyny występowania
zupełnie niezrozumiałego błędu – okazało się, że rysunek mógł być
generowany poprawnie dopiero po wyeliminowaniu rekurencji z kodu
źródłowego!), dlatego posłużyłem się pewną sztuczką, która pozwoliła mi
obejść konieczność rekurencyjnego wywoływania funkcji do obliczenia
centralnego położenia napisu. Mianowicie za każdym razem, gdy wykonywana
jest iteracja przez wszystkie punkty osi X (pętla <xsl:for-each>),
sprawdzana jest również informacja, czy jest to koniec poprzedniego dnia (i
jednocześnie rozpoczęcie nowego) – jeżeli tak, to wykonywana jest
procedura, która zlicza ilość punktów, które weszły w skład tego dnia, dzieli ją
przez dwa i wyznacza pozycję napisu na rysunku na podstawie numeru
bieżącej iteracji.
- 91/141 -
Do wyświetlania napisów (godziny oraz daty) wykorzystywany jest element
SVG <text>, natomiast linie pionowe, które mają na celu ułatwienie
odczytywania danych z wykresu, rysowane są za pomocą elementu SVG
<line>. Dla większej przejrzystości wykresu linie oznaczające rozpoczęcie
nowego dnia (godzina 24) rysowane są innym stylem niż pozostałe.
Dodatkowo w źródłowym dokumencie XML istnieje możliwość takiego
zagęszczenia wartości kolejnych punktów wykresu, aby nie wykreślano
pionowej linii dla każdego jednego punktu wykresu (aby to zrobić wystarczy
tylko zdefiniować więcej źródłowych elementów <point> niż istnieje
zdefiniowanych elementów <date>).
Sekcja opatrzona komentarzem „DOCUMENT MATCHING VALUES ELEMENT”
służy do wyświetlania liczb wzdłuż obu osi Y diagramu (lewej i prawej),
odpowiadających kolejnym pozycjom wartości wykresu, jak również
znajdujących się na tej samej wysokości poziomych linii, których funkcją jest
ułatwienie odczytywania wartości poszczególnych punktów wykresu. Metoda
generowania danych SVG jest dokładnie analogiczna jak dla osi X. Jedyną
różnicą jest fakt, że dodatkowe wartości są wykreślane nie tylko po lewej, ale
również po prawej stronie wykresu (o ile tylko zdefiniowano je w źródłowym
dokumencie XML).
Ostatnią sekcją arkusza stylu XSLT, kluczową dla całej aplikacji, jest
najdłuższy fragment kodu źródłowego, opisany jako „DOCUMENT MATCHING
DIAGRAM ELEMENT”. To tutaj przetwarzane są dane prowadzące do
wygenerowania właściwego wykresu, który jest najbardziej interesującą
częścią całej aplikacji. Kod źródłowy przeznaczony do zaaplikowania sekcji
<diagram> ze źródłowego dokumentu XML, zostaje zastosowany tyle razy,
ile razy pojawia się w nim element <diagram>. W związku z tym istotna jest
kolejność, w jakiej zapisywane są dane źródłowe, ponieważ w dokładnie
takiej kolejności będą one nanoszone na docelowy rysunek. Jeżeli chcemy,
- 92/141 -
aby na przykład wykres temperatury został wykreślony na pierwszym planie,
a wykres opadów na drugim, wówczas musimy przenieść dane pogody
przeznaczone dla wykresu temperatury (atrybut type="temperature") na
koniec danych w pliku XML.
Przetwarzanie danych źródłowych odbywa się kolejno dla każdej wartości
punktu w pętli <xsl:for-each>. Przed jej wywołaniem, ustawianych jest
kilka pomocniczych zmiennych lokalnych, które skracają skomplikowane
obliczenia w trakcie działania pętli. Są to zmienne przechowujące informacje
typowo obliczeniowe o tym, któremu wykresowi odpowiadają dane
(pamiętajmy, że na jednym diagramie istnieje możliwość wykreślenia dwóch
wykresów, dysponujących zupełnie różnymi zakresami osiąganych wartości),
jaki jest rozrzut możliwych wartości, jaka jest odległość między dwoma
sąsiednimi wartościami, tzn. wartościami różniącymi się o jedność, na rysunku
(w pikselach), jaka jest odległość pomiędzy dwoma kolejnymi argumentami
na rysunku (w pikselach), jak również informacje pobrane ze źródłowego
dokumentu XML o tym, jaki ma być typ, styl oraz kolor rysowanego wykresu.
Procedura przetwarzania danych jest identyczna dla każdego z punktów, przy
czym różni się ona w zależności od typu wykresu. W przypadku wykresu typu
temperaturowego w pierwszej kolejności wyznaczane są pozycje dwóch
sąsiednich punktów (x1,y1) i (x2,y2). Ustalane są one na podstawie
położenia wykresu na obrazie, obliczonych wcześniej pomocniczych stałych
oraz wartości punktów pobranych ze źródłowego dokumentu XML. Następnie
za pomocą elementu SVG <line> wykreślana jest linia łącząca oba te
punkty. Dodatkowo jeżeli w źródłowym dokumencie zdefiniowano
dopuszczalny błąd pomiaru poprzez podanie minimalnej oraz maksymalnej
wartości temperatury, obliczana jest dodatkowa linia pionowa na rysunku
(obliczane są tylko wartości, gdyż argument odpowiada wyznaczonej
wcześniej pozycji punktu x1), a następnie wykreślana przez element <line>
- 93/141 -
z identycznymi argumentami jak poprzednia z tym wyjątkiem, że atrybut
stroke-width (definiujący grubość linii) ma czterokrotnie mniejszą
wartość.
W przypadku wykresów opadów oraz wilgotności (typ wykresu to
precipitation lub humidity) obliczenia są nieco bardziej
skomplikowane, co wiąże się z faktem, że:
różne rodzaje wykresów mogą być przedstawiane na jednym diagramie
(patrz przykład w następnym podrozdziale: wykresy opadów oraz
wilgotności zamieszczone na tym samym rysunku), a zatem potrzebne
są do rozważenia dwa oddzielne przypadki – jeden, w którym wartości
punktów odnoszą się do lewej osi Y, drugi – do prawej osi Y
skala na osi Y może być z pewnych względów nieproporcjonalna (patrz
przykład w następnym podrozdziale: skala na wykresie wilgotności), a
zatem obliczenie właściwej pozycji punktu na wykresie wymaga
uwzględnienia odpowiedniego przedziału wartości, w którym znajduje
się aktualnie dany punkt, a następnie wyznaczeniu współrzędnych tego
przedziału na rysunku
Po obliczeniu położenia poszczególnych punktów na diagramie, tworzone są
odpowiadające wskazanemu stylowi wykresy. W przypadku wykresu
wilgotności jest to linia identyczna jak dla wykresu temperatury. Dla opadów
przelotnych są to pionowe linie (wykreślane podobnie jak różnica pomiędzy
minimalną a maksymalną temperaturą, z tym wyjątkiem, że biorą one swój
początek w wartości zerowej). Opady ciągłe oraz śnieg reprezentowane są w
postaci słupków, które tworzone są za pomocą elementu SVG <rect>, który
służy do wykreślania prostokątów.
- 94/141 -
4.2.4. Przykładowe wykresy
Mając już za sobą prezentację solidnej dawki teorii, która wprowadza w
zagadnienie generowania wykresów pogodowych od jej strony technicznej,
chciałbym poświęcić niniejszy fragment tekstu przedstawieniu dwóch
praktycznych przykładów wykorzystania omawianych wcześniej mechanizmów
oraz wygenerowaniu rysunków SVG, zawierających wykresy pogodowe,
stworzone w oparciu o dostarczone za pomocą źródłowych dokumentów XML
dane.
Jako pierwszy przykład chciałbym zaprezentować generację wykresu
temperatury. Przedstawię również wszystkie kroki prowadzące do
przygotowania dokumentu XML z danymi pogodowymi: opierając się na
regułach omówionych w podrozdziale 4.2.2 zamierzam pokazać, w jaki
sposób przygotować źródłowy dokument XML, zawierający dane, które
chcemy przedstawić na wykresie.
Rozpoczynamy od standardowej deklaracji XML w pierwszej linii dokumentu
oraz powiązania go z przygotowanym wcześniej arkuszem stylu XSLT w
następnej. Następnie należy utworzyć element nadrzędny (ang. root element
– korzeń) dokumentu. Zgodnie z definicją DTD jest to element <weather>.
Pamiętamy, że jeden z atrybutów tego elementu pozwala na określenie koloru
tła wykresu, korzystamy z tej właściwości i przypisujemy mu wybrany kolor.
W tym momencie mamy już przygotowany szkielet dokumentu XML, któremu
trzeba teraz dostarczyć odpowiednich danych:
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="weather.xsl"?><weather diagram_bckg_color="ghostwhite">...</weather>
Pierwszą sekcją dokumentu, którą dodamy, będzie tytuł wykresu. Jak
- 95/141 -
pamiętamy do tego celu należy wykorzystać element <description>.
Wewnątrz tego elementu wstawiamy element podrzędny <title>, którego
wartość stanowi sekcja CDATA. Dodatkowo powinniśmy określić kolor
obwódki dla tekstu na rysunku, korzystając z atrybutu outline_color. W
ten oto sposób mamy już zdefiniowaną pierwszą sekcję danych (sekcję
tytułową) dokumentu XML:
<description> <title outline_color="darkgreen"> <![CDATA[temperatura - temperature - Temperatur (°C)]]> </title></description>
Przystępujemy następnie do ustalenia, jaki okres czasu ma obejmować
wykres oraz jaką gęstość pomocniczych linii pionowych dla niego
przewidujemy. Przykładowo ustalmy, że kolejne linie pionowe wraz z
oznaczeniami godzin pojawiać się będą na rysunku co trzy godziny i
obejmować okres czasu od północy dnia 12 X do południa dnia 14 X. Sekcja
<dates> dokumentu XML będzie miała wówczas następującą postać:
<dates> <date day="12 X" hour="0"/> <date day="12 X" hour="3"/> <date day="12 X" hour="6"/> ... <date day="14 X" hour="12"/></dates>
W miejsce wielokropka wstawiamy kolejne godziny oraz dni, tak aby na
wykresie znalazło się ostatecznie 21 pionowych linii, ułatwiających
odczytywanie oraz umiejscawianie w czasie wybranych danych o
temperaturze.
Kolejna sekcja danych pozwala określić kilka elementów związanych z
- 96/141 -
wartościami, jakie mogą być przypisywane atrybutom wykresu. Są to:
maksymalna oraz minimalna wartość przedstawiana na wykresie, odstęp
pomiędzy kolejnymi wartościami oraz liczba poziomych linii pomocniczych,
ułatwiających odczyt wartości użytkownikowi. Dodatkowo atrybuty elementu
<values> pozwalają określić kolory obwódki tekstu wypisującego kolejne
wartości wzdłuż obu osi Y (lewej i prawej niezależnie).
W naszym przykładzie decydujemy się na zakres temperatur od 0°C do 30°C
wraz z dwoma poziomymi liniami pomocniczymi:
<values left_color_shadow="darkgray" right_color_shadow="darkgray"> <value left="0" right="0"/> <value left="10" right="10"/> <value left="20" right="20"/> <value left="30" right="30"/></values>
Pozostało nam jeszcze dostarczenie danych dla samego wykresu temperatury.
Aby zademonstrować wykreślanie dwóch wykresów (jednego typu) na tym
samym rysunku, przygotujmy dane dla temperatury uśrednionej oraz dla
temperatury odczuwanej. W przypadku temperatury uśrednionej dodamy
pomocnicze słupki określające dozwolony błąd prognozy, natomiast w
przypadku temperatury odczuwanej wykonamy zwykły wykres w postaci linii
ciągłej.
Jeżeli chcemy dostarczyć do dokumentu dane, zawierające prognozę pogody
w odstępach jednej godziny pomiędzy kolejnymi danymi, musimy wprowadzić
trzykrotnie więcej danych niż zrobiliśmy to w sekcji <dates>, ponieważ tam
odstępy pomiędzy kolejnymi punktami wykresu wynosiły trzy godziny. Chcąc
umieścić wykres uśredniony na pierwszym planie, musimy dane temperatury
odczuwanej wstawić do dokumentu wcześniej niż dane temperatury
uśrednionej.
- 97/141 -
Pozostało nam już tylko uzupełnić dane dla kolejnych punktów obu wykresów
oraz określić ich atrybuty (typ, styl, kolor, nazwa). W tym momencie proces
przygotowywania źródłowego dokumentu XML z pożądanymi danymi
pogodowymi został zakończony. Podgląd na dane obu wykresów temperatury
(uśrednionej oraz odczuwanej) można zobaczyć na rysunku 4.7, natomiast
pełny kod źródłowy tak przygotowanego dokumentu XML można przeglądać
w rozdziale 6.2.1. Efekt wygenerowanego za pomocą przygotowanego
wcześniej arkusza stylu XSLT wykresu możemy podziwiać na rysunku 4.8.
<diagram type="temperature" style="percepted" name="temperatura odczuwana" color="darkcyan" values="left"> <point value="8.0"/> <point value="7.8"/> ... <point value="20.5"/></diagram><diagram type="temperature" style="average" name="temperatura uśredniona" color="firebrick" values="right"> <point value="9.8" min_value="5.4" max_value="10.4"/> <point value="9.5" min_value="5.0" max_value="10.3"/> ... <point value="20.5" min_value="17.7" max_value="21.9"/></diagram>
Rys.4.7. Fragment kodu źródłowego (pominięto dane wewnętrznych punktówwykresu) dokumentu XML zawierającego dane, na podstawie których
generowany jest wykres temperatury (pełny kod źródłowy można znaleźć wrozdziale 6.2.1).
- 98/141 -
Rys.4.8. Wykres temperatury jako rysunek SVG wygenerowany na podstawieźródłowego dokumentu XML za pośrednictwem przygotowanego arkusza
stylu XSLT (podgląd wygenerowany w przeglądarce internetowej „Opera”).
W drugim przykładzie chciałbym przedstawić dwa zupełnie różne wykresy na
jednym diagramie. Uzyskanie takiego efektu jest możliwe dzięki opisanym
wcześniej mechanizmom, zaimplementowanym w dokumencie z danymi oraz
w arkuszu transformacji.
Wartości odnoszące się do danych opisanych wzdłuż lewej osi Y dotyczą
intensywności opadów (tutaj również wyróżnione zostaną różne style
wykresów: opady ciągłe deszczu, opady przelotne deszczu oraz opady
śniegu), natomiast prawa oś Y odnosi się do danych wilgotności powietrza.
Ponieważ budowa dokumentu XML dla każdego typu wykresu jest analogiczna
jak dla wykresu temperatury, skupię się wyłącznie do wskazaniu różnic
pomiędzy tymi dwoma dokumentami.
W elemencie nadrzędnym XML można określić kolor tła diagramu.
Skorzystajmy zatem z tej funkcjonalności i przypiszmy mu inny kolor, tym
razem jasnoniebieski:
<weather diagram_bckg_color="lightskyblue">...</weather>
- 99/141 -
Sekcja <description> również musi ulec zmianom, ponieważ powinna
uwzględniać nowy tytuł wykresu. Dodatkowo musi ona uwzględniać fakt, że
dwa zupełnie różne typy wykresów będą przedstawione na tym samym
rysunku. Konieczne jest zatem wstawienie dodatkowe elementu podrzędnego
<additional_title>:
<description> <title outline_color="steelblue"> <![CDATA[opady - precipitation - Niederschlag (mm/h : kg/m³h)]]> </title> <additional_title outline_color="darkgreen"> <![CDATA[wilgotność - humidity - Feuchtigkeit (%)]]> </additional_title></description>
Sekcja <dates> dokumentu XML pozostanie bez zmian, ponieważ chcemy,
aby opady oraz wilgotność dotyczyły dokładnie tego samego okresu, którego
dotyczył wykres temperaturowy. Natomiast w przypadku sekcji <values>
konieczne są zmiany, ponieważ możliwe do osiągnięcia wartości opadów oraz
wilgotności różnią się znacząco od temperatury. W związku z tym atrybutom
left elementów <value> przypiszemy zakres opadów od 0 do 8 mm3,
natomiast atrybutom right zakres wilgotności od 20 do 100%:
<values left_color_shadow="gray" right_color_shadow="steelblue"> <value left="0" right="20"/> <value left="0.2" right="40"/> <value left="0.5" right="60"/> <value left="1" right="80"/> <value left="2" right="90"/> <value left="4" right="95"/> <value left="6" right="98"/> <value left="8" right="100"/></values>
W ten oto sposób zdefiniowaliśmy „szkielet” diagramu dla wykresów opadów
- 100/141 -
oraz wilgotności. Pozostaje teraz tylko nanieść na niego odpowiednie
wartości, a arkusz stylu XSLT dokona właściwej transformacji i wygeneruje
dla nas gotowy rysunek SVG.
Chcemy przedstawić cztery różne wykresy. Sposób ich wykreślania na rysunku
determinowany jest przez wartości dwóch atrybutów (type oraz style)
elementu <diagram>. Dla wykresów opadowych
(type="precipitation") zastosujemy następujące style:
style="rainfall" dla ciągłego opadu deszczu, style="rainfall" dla
opadu śniegu (ten sam styl, ponieważ oba wykresy są wykreślane w ten sam
sposób; to, co je odróżnia, to kolor – dla deszczu wykres rysowany będzie w
kolorze niebieskim, dla śniegu w kolorze białym) oraz
style="occasional" dla przelotnego opadu deszczu. Dla wykresu
wilgotności (type="humidity") zastosowany zostanie styl liniowy:
style="line". Ostatecznie, po przypisaniu wartości kolejnym punktom
wykresu, w źródłowym dokumencie XML znaleźć będzie można dane, których
fragment przedstawiony został na rysunku 4.9, natomiast pełny kod źródłowy
tak przygotowanego dokumentu XML można przeglądać w rozdziale 6.2.2.
<diagram type="precipitation" style="rainfall" name="deszcz ciągły" color="cadetblue" values="left"> <point value="1.13"/> ... <point value="3.95"/></diagram><diagram type="precipitation" style="rainfall" name="śnieg" color="ghostwhite" values="left"> <point value="0.17"/> ... <point value="0.12"/></diagram><diagram type="precipitation" style="occasional" name="deszcz przelotny" color="darkcyan" values="left"> <point value="0.21"/> ... <point value="0.30"/>
- 101/141 -
</diagram><diagram type="humidity" style="line" name="wilgotność" color="darkgreen" values="right"> <point value="94.5"/> ... <point value="27.6"/></diagram>
Rys.4.9. Fragment kodu źródłowego (pominięto dane wewnętrznych punktówwykresu) dokumentu XML zawierającego dane, na podstawie których na
jednym rysunku generowane są wykresy opadów oraz wilgotności (pełny kodźródłowy można znaleźć w rozdziale 6.2.2).
Dysponując gotowymi danymi pogody, możemy przystąpić do zastosowania w
ich kontekście arkusza transformacji XSLT, w rezultacie którego otrzymamy
gotowy rysunek, który w przeglądarce internetowej będzie wyglądał mniej
więcej tak, jak przedstawiono to na rysunku 4.10.
Rys.4.10. Wykres opadów oraz wilgotności powietrza jako rysunek SVGwygenerowany na podstawie źródłowego dokumentu XML za pomocą arkusza
stylu XSLT (podgląd wygenerowany w przeglądarce internetowej „Opera”).
4.2.5. Ocena wykonania aplikacji
W rozdziale 4.2 starałem się szczegółowo i dokładnie opisać jedną z
przygotowanych na potrzeby mojej pracy dyplomowej praktycznych realizacji
- 102/141 -
zastosowania grafiki SVG w ramach potencjalnej aplikacji internetowej,
dostarczającej wizualizacji danych pogodowych w postaci wykresów.
Przedstawione pod koniec rozdziału przykłady udowadniają, że grafika SVG
świetnie spełnia swoją rolę w tym zadaniu i doskonale nadaje się do
prezentacji złożonych wykresów, charakteryzujących różnorodne dane
pogodowe, a odpowiednio dobrane komponenty obrazów dają w efekcie
przejrzyste i czytelne rysunki.
Sposób przygotowania arkusza transformacji XSLT oraz źródłowego
dokumentu XML pozwala na umieszczanie dwóch różnych typów wykresów na
jednym rysunku oraz dowolną liczbę różnych stylów wykresów
zdefiniowanych w obrębie tych typów. Od użytkownika aplikacji zależy tylko,
czy chce on różne rodzaje danych rozbijać na kilka oddzielnych wykresów, czy
też upakować wszystkie z nich na jednym tylko rysunku.
Już wcześniej zwróciłem uwagę na fakt, że dane grafiki zgromadzone w
formacie plików XML oraz XSLT, służących do wygenerowania rysunku SVG,
zajmują o wiele mniej przestrzeni dyskowej niż jakikolwiek rysunek rastrowy,
umożliwiający przedstawienie obrazów o porównywalnej jakości. W
konsekwencji dane przesyłane przez sieć Internet docierają do użytkownika
końcowego znacznie szybciej niż zwykłe obrazy typu JPG czy PNG, co ma
niebagatelny wpływ na jego zadowolenie z korzystania z wybranego serwisu
internetowego. Przypominam tę niezwykle ważną korzyść płynącą z
zastosowania grafiki SVG raz jeszcze, ponieważ uważam ją za jeden z
kluczowych argumentów, przemawiających za korzystaniem właśnie z grafiki
wektorowej zamiast używaniem popularnych i powszechnie stosowanych
obecnie standardów grafiki rastrowej.
4.3. Podsumowanie
Czwarty rozdział pracy dyplomowej zdecydowałem się w całości poświęcić
- 103/141 -
analizie praktycznego wykorzystania grafiki SVG. Odbyło się to poprzez
zrealizowanie kilku aplikacji, które mogłyby znaleźć swoje zastosowanie w
środowisku WWW. Wierzę, że przedstawione przeze mnie przykłady mogły w
sposób kompletny ugruntować pogląd na przydatność oraz niepodważalne zalety
zastosowania grafiki wektorowej SVG w Internecie.
W trakcie edycji kolejnych paragrafów swojej pracy, wielokrotnie wplatałem w
tekst różnorodne wnioski, płynące z analizy konkretnych funkcjonalności, a także
podkreślałem swoje własne poglądy na tematy dotyczące wykorzystania
dyskutowanego standardu w aplikacjach internetowych. Na pierwszy rzut oka
mogłoby się wydawać, że mój ogromny entuzjazm dla jego natychmiastowego
wykorzystania na wszystkich możliwych polach zastosowań, jest nieco
przesadzony. Jednak staranne rozpatrzenie wszystkich korzyści płynących z
wykorzystania każdego jednego małego elementu składowego standardu,
uświadamia nam potęgę możliwości, jaka drzemie w grafice SVG.
SVG może być już teraz bez trudu wykorzystane do budowania nowych lepszych
stron WWW. Obsługa standardu w wersji 1.1 jest bardzo dobra. W przypadku
przeglądarki „Opera” nie zauważyłem praktycznie żadnych wad, które
dyskwalifikowałyby ją z tego zastosowania. Również obsługa SVG w
przeglądarce „Mozilla Firefox” poprawia się wraz z publikacją każdej kolejnej jej
wersji. Czarną owcą w gronie najpopularniejszych przeglądarek internetowych
pozostaje „Internet Explorer” (nic dziwnego, skoro Microsoft forsuje w nim swój
własny standard grafiki wektorowej VML), na szczęście wciąż można
zainstalować w nim odpowiednią wtyczkę, umożliwiającą obsługę grafiki SVG.
Życzyłbym sobie, aby przedstawione przeze mnie w tym rozdziale praktyczne
przykłady realizacji aplikacji internetowych, korzystających z możliwości SVG,
stanowiły zachętę dla innych do dokładniejszego zapoznania się z tym
standardem oraz do śmielszego sięgnięcia po jego wykorzystanie we własnych
projektach. Zadowolenie autora witryny internetowej z dobrze wykonanej pracy
- 104/141 -
może iść śmiało w parze z zadowoleniem odwiedzających ją gości. Jeśli zsumuje
się wszystkie zalety, jakie płyną z wykorzystania będącego przedmiotem mojej
pracy dyplomowej standardu, nie musi to być pusty i nic nie znaczący slogan.
Pracując nad wykonaniem prezentowanych tutaj aplikacji, zdobyłem praktyczne
doświadczenie, umożliwiające mi swobodne operowanie najważniejszymi
możliwościami standardu SVG. Na tej podstawie mogę bez przymusu stwierdzić,
że tworzenie grafiki w oparciu o niego jest bardzo wygodne, nieskomplikowane,
żeby nie powiedzieć banalne. Oczywiście korzystanie z bardziej zaawansowanych
funkcji SVG wymaga większej wprawy, doświadczenia oraz pomysłowości.
Niemniej jednak do realizacji podstawowych celów ich znajomość nie jest
przecież konieczna.
Zastosowanie SVG we własnych aplikacjach mógłbym streścić w kilku słowach:
prostota i elegancja, zarówno podczas projektowania, jak i późniejszego ich
wykonania. Mam nadzieję, że solidnym dowodem na poparcie tych słów są
przykłady moich prac, przedstawionych oraz szczegółowo przeanalizowanych w
tym rozdziale.
- 105/141 -
5. Zakończenie
Przystępując do napisania niniejszej pracy dyplomowej, dwa główne cele, jakie
sobie postawiłem, stanowiły dokładne rozpoznanie grafiki wektorowej SVG oraz
praktyczną realizację kilku aplikacji, korzystających z jej możliwości. Mam nadzieję,
że wiedza teoretyczna poparta licznymi przykładami, którą starałem się jak
najlepiej przedstawić w części przeglądowej pracy, daje solidny obraz tego, co
można uzyskać, posługując się grafiką wektorową. Trzeci rozdział mojej pracy
może stanowić doskonały punkt wyjścia do zgłębiania tajników SVG dla każdej
osoby rozpoczynającej swoją przygodę z wykorzystaniem grafiki wektorowej.
Jestem przekonany, że zebrane we wspomnianym rozdziale informacje mogą
stanowić wartościowe źródło wiedzy na opisywany temat.
Być może aplikacje zrealizowane na potrzeby części praktycznej tej pracy nie są
szczególnie imponujące, biorąc pod uwagę fantastyczne możliwości, jakie daje
- 106/141 -
doświadczonemu artyście standard SVG. Chciałbym jednak zwrócić uwagę na fakt,
iż wykonałem je całkowicie samodzielnie. Nie wzorowałem się w trakcie ich
opracowywania na żadnych innych gotowych aplikacjach, mogących realizować
podobne funkcje. Kod źródłowy poszczególnych aplikacji został przygotowany
wyłącznie na potrzeby tej pracy. Od samego początku zamierzałem stworzyć
produkt oryginalny i ten cel z całą pewnością osiągnąłem, z czego mogę być
bardzo zadowolony.
Z całą pewnością pole do popisu w dziedzinie grafiki wektorowej jest ogromne.
Wielokrotnie starałem się wykazać, jak potężne możliwości tkwią w SVG. Tworzenie
grafiki w tym formacie jest limitowane praktycznie tylko i wyłącznie talentem i
wyobraźnią twórcy. Liczba możliwych do zrealizowania aplikacji, korzystających z
SVG, jest praktycznie niczym nieograniczona. Wszędzie, gdzie tylko istnieje
potrzeba wizualizacji jakiejkolwiek informacji, można tego dokonać za pomocą
grafiki SVG. Jej zastosowanie da w efekcie oszczędność czasu (dzięki prostocie i
szybkości generowania rysunków), zasobów dyskowych (niewielkie rozmiary
plików) oraz nerwów (dzięki małym rozmiarom rysunki SVG przesyłane są przez
sieć w o wiele krótszym czasie niż ich rastrowe odpowiedniki), a jednocześnie
pozwoli na generowanie obrazów o najwyższej jakości (dzięki wektorowej
reprezentacji danych), bezstratnie dających się pomniejszać lub powiększać (dzięki
ich skalowalności). Dokładne zapoznanie się ze standardem SVG pozwala mi na tak
wielki entuzjazm w ocenie możliwych jego zastosowań.
Niniejsza praca dyplomowa w żadnym wypadku nie zamyka tematu analizy
obszarów możliwego wykorzystania grafiki SVG. Dalsze kierunki badań powinny
dotyczyć między innymi szczegółowego przeglądu wszystkich detali, opisywanych
w rekomendacji W3C. W swojej pracy skupiłem się na przeglądzie standardu jako
całości, ale z całą pewnością równie obszerna mogłaby być szczegółowa analiza
wybranych tylko zagadnień funkcjonalnych SVG, np. filtrów lub problemów
związanych z wyświetlaniem tekstu na rysunkach. Niemal każdy rozdział
rekomendacji obejmuje na tyle szerokie spektrum zagadnień, że wart jest
- 107/141 -
starannej i szczegółowej analizy.
Oprócz zagadnień ściśle teoretycznych bardzo wiele można zrealizować jeszcze na
polu praktycznym. Naturalnie oczywisty wydaje się być rozwój zupełnie nowych
aplikacji, korzystających z grafiki wektorowej SVG. Duże pole do popisu istnieje
jednak również w zakresie rozbudowy przygotowanych przeze mnie w ramach tej
pracy aplikacji. Możliwa jest ich rozbudowa o nowe funkcjonalności, które
sygnalizowałem już w treści czwartego rozdziału pracy. Warto byłoby z całą
pewnością opracować dedykowane ich obsłudze interfejsy administracyjne na
poziomie WWW, które mogłyby służyć do zautomatyzowanego generowania
różnorodnych wykresów. Innym cennym pomysłem mogłoby być opracowanie
stron WWW korzystających z generowanych przez moje aplikacje wykresów
statystycznych lub pogodowych, wstawiających rysunki SVG do swojej zawartości.
Bliższe poznanie standardu SVG wzbudziło mój wielki entuzjazm. Chciałbym, aby
ten entuzjazm udzielił się również czytelnikom niniejszej pracy dyplomowej. Jestem
przekonany, że w przyszłości skorzystam nieraz z możliwości oferowanych przez
grafikę SVG w trakcie projektowania swoich własnych witryn WWW oraz innych
aplikacji internetowych.
- 108/141 -
6. Dodatki: kody źródłowe
W dodatkowym rozdziale mojej pracy dyplomowej postanowiłem zamieścić kody
źródłowe wszystkich zrealizowanych przeze mnie (w ramach części praktycznej)
aplikacji, które w sposób szczegółowy zostały omówione w rozdziale czwartym.
6.1. Generator wykresów statystycznych
W tej sekcji zawarte zostały wszystkie kody źródłowe przygotowane w trakcie
opracowywania generatora wykresów statystycznych.
6.1.1. Moduł (klasa) ChartSVG do generowania wykresów
Kod źródłowy modułu „ChartSVG” opracowałem w języku skryptowym Perl:
package ChartSVG;
- 109/141 -
use Carp;
# Lewy gorny rog polozenia calego rysunku oraz jego calkowita wysokosc:use constant _ALL_LEFT => 3;use constant _ALL_TOP => 3;use constant _ALL_HEIGHT => 300;# Szerokosc pikselowa, poswiecana na jeden slupek diagramu oraz szerokosc wlasciwa slupka:use constant _FOR_BAR_WIDTH => 45;use constant _BAR_WIDTH => 40;use constant _FREE_SPACE_BARS => _FOR_BAR_WIDTH - _BAR_WIDTH;# Minimalna szerokosc wykresu, jezeli nie wprowadzono zadnych danych:use constant _DIAGRAM_MIN_WIDTH => 50;# Odleglosc poziomu poczatku slupkow (i dolnej osi opisu) od dolnej krawedzi ramki:use constant _SPACE_AT_BOTTOM => 80;# Odleglosc najwyzszego poziomu slupkow (i miejsca na tytul wykresu)od gornej krawedzi ramki:use constant _SPACE_AT_TOP => 30;# Dodatkowe miejsce na wpisanie wartosci powyzej najwyzszego slupka:use constant _SPACE_AT_BAR_TOP => 10;
sub new { my $proto = shift; my $class = ref( $proto ) || $proto; my $self = {}; $self->{HOWMANY} = 0; $self->{SIZE} = 1; $self->{PERCENTAGE} = 0; $self->{MAX_ENTRIES_ON_DIAGRAM} = 10; $self->{ENTRIES} = []; $self->{TITLE} = "You Haven't Set The Diagram Title"; $self->{AXIS_Y_DESC} = ""; bless( $self, $class ); return $self;}
sub set_y_desc { my $self = shift; $self->{AXIS_Y_DESC} = shift;}
sub add { my $self = shift; my( $name, $number ) = @_; # Test if $number really contains a number: if( !( $number =~ m/^\d*?\.?\d*?$/ ) ) { carp "FATAL ERROR: entry not added ($number is not a number!)"; return; } # Create a new hash containing the information on newly added entry: my %new_entry; $new_entry{"NAME"} = $name;
- 110/141 -
$new_entry{"NUMBER"} = $number; # Add this hash to the ENTRIES table through reference: $self->{ENTRIES}[$self->{HOWMANY}] = \%new_entry; ++$self->{HOWMANY};}
sub set_title { my $self = shift; $self->{TITLE} = shift;}
sub entries { my $self = shift; return $self->{HOWMANY};}
sub show_all { my $self = shift; for( my $i=0; $i<$self->{HOWMANY}; $i++ ) { printf "name: %-20s number: %-5.2f\n", $self->{ENTRIES}[$i]{"NAME"}, $self->{ENTRIES}[$i]{"NUMBER"}; }}
sub sort_all { my $self = shift; for( my $i=($self->{HOWMANY}-1); $i>=0; $i-- ) { for( my $j=0; $j<$i; $j++ ) { if( $self->{ENTRIES}[$j]{"NUMBER"} < $self->{ENTRIES}[$j+1]{"NUMBER"} ) { ( $self->{ENTRIES}[$j], $self->{ENTRIES}[$j+1] ) = ( $self->{ENTRIES}[$j+1], $self->{ENTRIES}[$j] ); } } }}
sub max { my $a = shift; my $b = shift; return $a > $b ? $a : $b; }sub MAX { my( $max ) = pop( @_ ); foreach my $foo ( @_ ) { $max = $foo if $max < $foo; } return $max; }sub min { my $a = shift; my $b = shift; return $a < $b ? $a : $b; }sub MIN { my( $min ) = pop( @_ ); foreach my $foo ( @_ ) { $min = $foo if $min > $foo; } return $min; }
sub calc_y_val { my $self = shift; my $max_val = shift; my @values; # Dziele wartosc maksymalna przez 10 tak dlugo, az uda mi sie uzyskac wartosc mniejsza niz 10: my $new_max = $max_val; my $przez_ile_dzielone = 1; while( $new_max > 10 ) { $przez_ile_dzielone *= 10; $new_max /= 10; }
- 111/141 -
# Jesli uzyskana wartosc jest mniejsza niz pewna okreslona wartosc,to mnoze ja celem powiekszenia ilosci kreseczek na osi Y: if( $new_max <= 2 ) { $przez_ile_dzielone /= 5; $new_max *= 5; } elsif( $new_max <= 5 ) { $przez_ile_dzielone /= 2; $new_max *= 2; } # Bierzemy tylko calkowite czesci z wszystkich liczb <= $new_max: my $i; $przez_ile_dzielone = int( $przez_ile_dzielone ); for( $i=0; $i<=$new_max; $i++ ) { $values[$i] = $i * $przez_ile_dzielone; } # $values[$i] = $max_val; return @values;}
sub make_image { my $self = shift; # $img_type: WHEEL -> kolowy, BAR -> slupkowy my( $filename, $img_type ) = @_; # Prepare miscellaneous image variables: my $size = $self->{SIZE}; my $value_type; if( $self->{PERCENTAGE} == 0 ) { $value_type = 'NUMBER'; } else { $value_type = 'PERCENT'; } # Sort all entries ascending: $self->sort_all(); # Open the target file for writing: open( SVG, ">$filename" ); # Prepare the SVG headers: my $svg = "<?xml version=\"1.0\" encoding=\"windows-1250\" standalone=\"no\" ?>\n"; $svg .= "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"; # Okresl szerokosc wlasciwego diagramu: my $szerokosc = min( $self->{HOWMANY}, $self->{MAX_ENTRIES_ON_DIAGRAM} ) * _FOR_BAR_WIDTH + _DIAGRAM_MIN_WIDTH; my $viewBoxCx = $szerokosc + _ALL_LEFT * 2; my $viewBoxCy = _ALL_HEIGHT + _ALL_TOP * 2; $svg .= "<svg width=\"100%\" height=\"100%\" viewBox=\"0 0 $viewBoxCx $viewBoxCy\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n"; # Narysuj podstawowe tlo pod calym obszarem rysunku: my( $all_left, $all_top, $all_right, $all_bottom ) = ( 0, 0, $viewBoxCx, $viewBoxCy ); $svg .= " <path stroke=\"#117711\" fill=\"#AADDAA\" d=\"M $all_left$all_top L $all_right $all_top L $all_right $all_bottom L $all_left $all_bottom Z\" />\n"; # Okresl wielkosc diagramu: my( $ins_left, $ins_top, $ins_right, $ins_bottom ) = ( _ALL_LEFT, _ALL_TOP, $szerokosc + _ALL_LEFT, _ALL_HEIGHT + _ALL_TOP ); # Wyrysuj obramowanie diagramu: # $svg .= " <rect x=\"$left\" y=\"$top\" width=\"$width\" height=\"$height\" stroke=\"#228822\" fill=\"#EEFFEE\" />\n"; $svg .= " <path stroke=\"#228822\" fill=\"#EEFFEE\" d=\"M $ins_left$ins_top L $ins_right $ins_top L $ins_right $ins_bottom L $ins_left $ins_bottom Z\" />"; # Wypisz na gorze tytul wykresu: my( $txt_head_y, $txt_head_x ) = ( _ALL_TOP + _SPACE_AT_TOP - 7,
- 112/141 -
$ins_left + ( $ins_right - $ins_left ) / 2 - 2 ); $svg .= " <text x=\"$txt_head_x\" y=\"$txt_head_y\" font-family=\"Verdana\" text-anchor=\"middle\" font-size=\"21\" fill=\"#006600\">"; $svg .= $self->{TITLE}; $svg .= "</text>\n"; # Check out the diagram type: if( $img_type eq "WHEEL" ) { # Generate wheeled SVG chart: print "Generating wheeled SVG chart...\n"; # Current diagram: $svg .= " <circle cx=\"200\" cy=\"200\" r=\"180\" stroke=\"red\" stroke-width=\"20\" fill=\"black\" />\n"; print "OK!\n"; } elsif( $img_type eq "BAR" ) { # Generate bar SVG chart: print "Generating bar SVG chart..."; # Define the vertical linear gradient for diagrams: $svg .= " <defs>\n"; $svg .= " <linearGradient id=\"gradient_01\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n"; $svg .= " <stop offset=\"0%\" style=\"stop-color:rgb(32,32,255);stop-opacity:1\" />\n"; $svg .= " <stop offset=\"100%\" style=\"stop-color:rgb(0,0,128);stop-opacity:1\" />\n"; $svg .= " </linearGradient>\n"; $svg .= " </defs>\n"; $svg .= " <defs>\n"; $svg .= " <linearGradient id=\"gradient_02\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n"; $svg .= " <stop offset=\"0%\" style=\"stop-color:rgb(64,64,255);stop-opacity:1\" />\n"; $svg .= " <stop offset=\"100%\" style=\"stop-color:rgb(16,16,255); stop-opacity:1\" />\n"; $svg .= " </linearGradient>\n"; $svg .= " </defs>\n"; # Znajdz najwieksza wartosc sposrod wszystkich elementow: my @temp; for( my $i=0; $i<$self->{HOWMANY}; $i++ ) { $temp[$i] = $self->{ENTRIES}[$i]{"NUMBER"}; } my $max_wartosc = MAX( @temp ); # Okresl obramowanie wykresu: my( $dia_left, $dia_top, $dia_right, $dia_bottom ) = ( $ins_left +_DIAGRAM_MIN_WIDTH - _FREE_SPACE_BARS, $ins_top + _SPACE_AT_TOP - 1,$ins_right - _FREE_SPACE_BARS, $ins_bottom - _SPACE_AT_BOTTOM ); # Linia pionowa na wartosci i linia pozioma na kolejne elementy: $svg .= "<polyline fill=\"none\" stroke=\"#222222\" stroke-width=\"2\" points=\"$dia_left,$dia_top $dia_left,$dia_bottom $dia_right,$dia_bottom\" />"; # Oblicz wartosci, ktore beda wyswietlane na osi Y: my @values = $self->calc_y_val( $max_wartosc ); my $jednostka_wysokosci = ( $dia_bottom - $dia_top - _SPACE_AT_BAR_TOP ) / $max_wartosc; my( $left_kreseczka, $right_kreseczka ) = ( $dia_left - 2, $dia_left + 2 ); for( my $i=0; $i<@values; $i++ ) { # Wypisz kreseczke pozioma na osi Y, symbolizujacac poziom
- 113/141 -
wartosci na wykresie: my $poz_y = $dia_bottom - int( $jednostka_wysokosci * $values[$i]); $svg .= " <path stroke=\"#222222\" fill=\"none\" d=\"M $left_kreseczka $poz_y L $right_kreseczka $poz_y\" />\n"; # Wypisz liczbe okreslajaca wartosc na tym poziomie: my( $value_number_x, $value_number_y ) = ( $right_kreseczka - 6, $poz_y + 4 ); $svg .= " <text x=\"$value_number_x\" y=\"$value_number_y\" font-family=\"Tahoma\" font-weight=\"bold\" text-anchor=\"end\" font-size=\"10\" fill=\"#117711\">"; $svg .= $values[$i]; if( $self->{PERCENTAGE} == 1 ) { $svg .= "%"; } $svg .= "</text>\n"; } # Wypisz na gorze osi Y nazwe wyswietlanych jednostek: if( $self->{AXIS_Y_DESC} ) { my( $pos_x_desc, $pos_y_desc ) = ( $right_kreseczka - 4, $dia_top+ 4 ); $svg .= " <text x=\"$pos_x_desc\" y=\"$pos_y_desc\" font-family=\"Tahoma\" font-weight=\"bold\" text-anchor=\"end\" font-size=\"8\" fill=\"#117711\">["; $svg .= $self->{AXIS_Y_DESC}; $svg .= "]</text>\n"; } # Znajdz wielkosci zmiennych potrzebne do wyrysowywania kolejnych slupkow: my $max_wysokosc = $ins_bottom - $ins_top - _SPACE_AT_TOP - _SPACE_AT_BAR_TOP - _SPACE_AT_BOTTOM; my $jednostka_wysokosci = $max_wysokosc / $max_wartosc; # Wyrysuj slupek dla kazdego elementu: for( my $i=0; $i<$self->{HOWMANY}; $i++ ) { last if( $i == $self->{MAX_ENTRIES_ON_DIAGRAM} ); # Zbuduj slupek dla wybranego elementu: my $slupek_left = $ins_left + _DIAGRAM_MIN_WIDTH + $i * _FOR_BAR_WIDTH; my $slupek_width = _BAR_WIDTH; my $slupek_height = int( $jednostka_wysokosci * $self->{ENTRIES}[$i]{"NUMBER"} ); my $slupek_top = $ins_bottom - _SPACE_AT_BOTTOM - $slupek_height - 1; my $gradient; if( $i % 2 ) { $gradient = "gradient_01"; } else { $gradient = "gradient_02"; } $svg .= " <rect x=\"$slupek_left\" y=\"$slupek_top\" width=\"$slupek_width\" height=\"$slupek_height\" style=\"fill:url(#$gradient)\" />\n"; # Wypisz tekst opisujacy kazdy ze slupkow u jego dolu: my( $txt_pos_x, $txt_pos_y ) = ( $slupek_left + _BAR_WIDTH / 2, $ins_bottom - _SPACE_AT_BOTTOM + 12 ); $svg .= " <g transform=\"translate($txt_pos_x $txt_pos_y)\">\n"; $svg .= " <g transform=\"rotate(-50)\">\n"; $svg .= " <text x=\"0\" y=\"0\" font-family=\"Tahoma\" text-anchor=\"end\" font-size=\"14\" fill=\"#117711\">"; $svg .= $self->{ENTRIES}[$i]{"NAME"}; $svg .= "</text>\n"; $svg .= " </g>\n";
- 114/141 -
$svg .= " </g>\n"; # Wpisz wartosc dla kazdego ze slupkow ponad nim: my( $number_x, $number_y ) = ( $slupek_left + int( $slupek_width / 2 ) + 1, $slupek_top - 2 ); $svg .= " <text x=\"$number_x\" y=\"$number_y\" font-family=\"Tahoma\" font-weight=\"bold\" text-anchor=\"middle\" font-size=\"10\" fill=\"#117711\">"; $svg .= sprintf( "%.2f", $self->{ENTRIES}[$i]{"NUMBER"} ); # Dopisujemy za liczba znak procenta, jezeli jest to wykres procentowy: if( $self->{PERCENTAGE} == 1 ) { $svg .= "%"; } $svg .= "</text>\n"; } print "OK!\n"; } $svg .= "</svg>"; print SVG $svg; close( SVG );}
1;
6.1.2. Porównanie liczby ludności państw świata
Kod źródłowy generujący rysunek SVG przedstawiający porównanie liczby
ludności w najludniejszych państwach świata opracowałem w języku
skryptowym Perl w oparciu o moduł „ChartSVG”:
#!/usr/bin/perl
use strict;use ChartSVG;
my $myChart = ChartSVG->new();$myChart->set_title( "LUDNOŚĆ PAŃSTW ŚWIATA" );$myChart->set_y_desc( "mln" );$myChart->add( "Chiny", 1306.3 );$myChart->add( "Indie", 1080.3 );$myChart->add( "USA", 295.7 );$myChart->add( "Indonezja", 242.0 );$myChart->add( "Brazylia", 186.1 );$myChart->add( "Pakistan", 162.4 );$myChart->add( "Bangladesz", 144.3 );$myChart->add( "Rosja", 143.4 );$myChart->add( "Nigeria", 128.8 );$myChart->add( "Japonia", 127.4 );
$myChart->make_image( "kraje.svg", "BAR" );
Kod źródłowy wygenerowanego za pomocą powyższego skryptu rysunku
- 115/141 -
SVG:
<?xml version="1.0" encoding="windows-1250" standalone="no" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 506 306" version="1.1" xmlns="http://www.w3.org/2000/svg"> <path stroke="#117711" fill="#AADDAA" d="M 0 0 L 506 0 L 506 306 L 0 306 Z" /> <path stroke="#228822" fill="#EEFFEE" d="M 3 3 L 503 3 L 503 303 L 3 303 Z" /> <text x="251" y="26" font-family="Verdana" text-anchor="middle" font-size="21" fill="#006600">LUDNOŚĆ PAŃSTW ŚWIATA</text> <defs> <linearGradient id="gradient_01" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" style="stop-color:rgb(32,32,255); stop-opacity:1" /> <stop offset="100%" style="stop-color:rgb(0,0,128); stop-opacity:1" /> </linearGradient> </defs> <defs> <linearGradient id="gradient_02" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" style="stop-color:rgb(64,64,255); stop-opacity:1" /> <stop offset="100%" style="stop-color:rgb(16,16,255); stop-opacity:1" /> </linearGradient> </defs><polyline fill="none" stroke="#222222" stroke-width="2" points="48,32 48,223 498,223" /> <path stroke="#222222" fill="none" d="M 46 223 L 50 223" /> <text x="44" y="227" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">0</text> <path stroke="#222222" fill="none" d="M 46 196 L 50 196" /> <text x="44" y="200" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">200</text> <path stroke="#222222" fill="none" d="M 46 168 L 50 168" /> <text x="44" y="172" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">400</text> <path stroke="#222222" fill="none" d="M 46 140 L 50 140" /> <text x="44" y="144" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">600</text> <path stroke="#222222" fill="none" d="M 46 113 L 50 113" /> <text x="44" y="117" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">800</text> <path stroke="#222222" fill="none" d="M 46 85 L 50 85" /> <text x="44" y="89" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">1000</text> <path stroke="#222222" fill="none" d="M 46 57 L 50 57" /> <text x="44" y="61" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">1200</text> <text x="46" y="36" font-family="Tahoma" font-weight="bold" text-
- 116/141 -
anchor="end" font-size="8" fill="#117711">[mln]</text> <rect x="53" y="42" width="40" height="180" style="fill:url(#gradient_02)" /> <g transform="translate(73 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">Chiny</text> </g> </g> <text x="74" y="40" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">1306.30</text> <rect x="98" y="74" width="40" height="148" style="fill:url(#gradient_01)" /> <g transform="translate(118 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">Indie</text> </g> </g> <text x="119" y="72" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">1080.30</text> <rect x="143" y="182" width="40" height="40" style="fill:url(#gradient_02)" /> <g transform="translate(163 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">USA</text> </g> </g> <text x="164" y="180" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">295.70</text> <rect x="188" y="189" width="40" height="33" style="fill:url(#gradient_01)" /> <g transform="translate(208 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">Indonezja</text> </g> </g> <text x="209" y="187" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">242.00</text> <rect x="233" y="197" width="40" height="25" style="fill:url(#gradient_02)" /> <g transform="translate(253 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">Brazylia</text> </g> </g> <text x="254" y="195" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">186.10</text> <rect x="278" y="200" width="40" height="22" style="fill:url(#gradient_01)" /> <g transform="translate(298 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-
- 117/141 -
size="14" fill="#117711">Pakistan</text> </g> </g> <text x="299" y="198" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">162.40</text> <rect x="323" y="203" width="40" height="19" style="fill:url(#gradient_02)" /> <g transform="translate(343 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">Bangladesz</text> </g> </g> <text x="344" y="201" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">144.30</text> <rect x="368" y="203" width="40" height="19" style="fill:url(#gradient_01)" /> <g transform="translate(388 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">Rosja</text> </g> </g> <text x="389" y="201" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">143.40</text> <rect x="413" y="205" width="40" height="17" style="fill:url(#gradient_02)" /> <g transform="translate(433 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">Nigeria</text> </g> </g> <text x="434" y="203" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">128.80</text> <rect x="458" y="205" width="40" height="17" style="fill:url(#gradient_01)" /> <g transform="translate(478 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">Japonia</text> </g> </g> <text x="479" y="203" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">127.40</text></svg>
6.1.3. Porównanie wyników wyborów parlamentarnych
Kod źródłowy generujący rysunek SVG przedstawiający fikcyjne wyniki
wyborów parlamentarnych w 2006 roku opracowałem w języku skryptowym
- 118/141 -
Perl w oparciu o moduł „ChartSVG”:
#!/usr/bin/perl
use strict;use ChartSVG;
my $myChart = ChartSVG->new();$myChart->set_title( "WYBORY PARLAMENTARNE 2006" );$myChart->set_y_desc( "%" );$myChart->add( "SLD", "7.86" );$myChart->add( "UPR", 5.76 );$myChart->add( "PO", 22.19 );$myChart->add( "PiS", 21.22 );$myChart->add( "SdPL", 2.83 );$myChart->add( "LPR", 4.87 );$myChart->add( "Samoobrona", 6.01 );$myChart->add( "PSL", 4.14 );$myChart->add( "KPEiR", 0.24 );$myChart->add( "PD", 2.35 );
$myChart->make_image( "wybory.svg", "BAR" );
Kod źródłowy wygenerowanego za pomocą powyższego skryptu rysunku
SVG:
<?xml version="1.0" encoding="windows-1250" standalone="no" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 506 306" version="1.1" xmlns="http://www.w3.org/2000/svg"> <path stroke="#117711" fill="#AADDAA" d="M 0 0 L 506 0 L 506 306 L 0 306 Z" /> <path stroke="#228822" fill="#EEFFEE" d="M 3 3 L 503 3 L 503 303 L 3 303 Z" /> <text x="251" y="26" font-family="Verdana" text-anchor="middle" font-size="21" fill="#006600">WYBORY PARLAMENTARNE 2006</text> <defs> <linearGradient id="gradient_01" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" style="stop-color:rgb(32,32,255); stop-opacity:1" /> <stop offset="100%" style="stop-color:rgb(0,0,128); stop-opacity:1" /> </linearGradient> </defs> <defs> <linearGradient id="gradient_02" x1="0%" y1="0%" x2="0%" y2="100%"> <stop offset="0%" style="stop-color:rgb(64,64,255); stop-opacity:1" /> <stop offset="100%" style="stop-color:rgb(16,16,255); stop-opacity:1" />
- 119/141 -
</linearGradient> </defs><polyline fill="none" stroke="#222222" stroke-width="2" points="48,32 48,223 498,223" /> <path stroke="#222222" fill="none" d="M 46 223 L 50 223" /> <text x="44" y="227" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">0</text> <path stroke="#222222" fill="none" d="M 46 183 L 50 183" /> <text x="44" y="187" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">5</text> <path stroke="#222222" fill="none" d="M 46 142 L 50 142" /> <text x="44" y="146" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">10</text> <path stroke="#222222" fill="none" d="M 46 101 L 50 101" /> <text x="44" y="105" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">15</text> <path stroke="#222222" fill="none" d="M 46 60 L 50 60" /> <text x="44" y="64" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="10" fill="#117711">20</text> <text x="46" y="36" font-family="Tahoma" font-weight="bold" text-anchor="end" font-size="8" fill="#117711">[%]</text> <rect x="53" y="42" width="40" height="180" style="fill:url(#gradient_02)" /> <g transform="translate(73 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">PO</text> </g> </g> <text x="74" y="40" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">22.19</text> <rect x="98" y="50" width="40" height="172" style="fill:url(#gradient_01)" /> <g transform="translate(118 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">PiS</text> </g> </g> <text x="119" y="48" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">21.22</text> <rect x="143" y="159" width="40" height="63" style="fill:url(#gradient_02)" /> <g transform="translate(163 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">SLD</text> </g> </g> <text x="164" y="157" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">7.86</text> <rect x="188" y="174" width="40" height="48" style="fill:url(#gradient_01)" /> <g transform="translate(208 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-
- 120/141 -
size="14" fill="#117711">Samoobrona</text> </g> </g> <text x="209" y="172" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">6.01</text> <rect x="233" y="176" width="40" height="46" style="fill:url(#gradient_02)" /> <g transform="translate(253 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">UPR</text> </g> </g> <text x="254" y="174" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">5.76</text> <rect x="278" y="183" width="40" height="39" style="fill:url(#gradient_01)" /> <g transform="translate(298 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">LPR</text> </g> </g> <text x="299" y="181" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">4.87</text> <rect x="323" y="189" width="40" height="33" style="fill:url(#gradient_02)" /> <g transform="translate(343 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">PSL</text> </g> </g> <text x="344" y="187" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">4.14</text> <rect x="368" y="200" width="40" height="22" style="fill:url(#gradient_01)" /> <g transform="translate(388 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">SdPL</text> </g> </g> <text x="389" y="198" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">2.83</text> <rect x="413" y="203" width="40" height="19" style="fill:url(#gradient_02)" /> <g transform="translate(433 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">PD</text> </g> </g> <text x="434" y="201" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">2.35</text> <rect x="458" y="221" width="40" height="1"
- 121/141 -
style="fill:url(#gradient_01)" /> <g transform="translate(478 235)"> <g transform="rotate(-50)"> <text x="0" y="0" font-family="Tahoma" text-anchor="end" font-size="14" fill="#117711">KPEiR</text> </g> </g> <text x="479" y="219" font-family="Tahoma" font-weight="bold" text-anchor="middle" font-size="10" fill="#117711">0.24</text></svg>
6.2. Generator wykresów pogodowych
W tej sekcji zawarte zostały wszystkie kody źródłowe przygotowane w trakcie
opracowywania generatora wykresów pogodowych.
6.2.1. Dane XML do wykresu temperatury
Dane temperatury zebrane do pliku w formacie XML:
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="weather.xsl"?><weather diagram_bckg_color="ghostwhite"> <description> <title outline_color="darkgreen"><![CDATA[temperatura - temperature - Temperatur (°C)]]></title> </description> <dates> <date day="12 X" hour="0"/> <date day="12 X" hour="3"/> <date day="12 X" hour="6"/> <date day="12 X" hour="9"/> <date day="12 X" hour="12"/> <date day="12 X" hour="15"/> <date day="12 X" hour="18"/> <date day="12 X" hour="21"/> <date day="13 X" hour="0"/> <date day="13 X" hour="3"/> <date day="13 X" hour="6"/> <date day="13 X" hour="9"/> <date day="13 X" hour="12"/> <date day="13 X" hour="15"/> <date day="13 X" hour="18"/> <date day="13 X" hour="21"/> <date day="14 X" hour="0"/> <date day="14 X" hour="3"/> <date day="14 X" hour="6"/> <date day="14 X" hour="9"/> <date day="14 X" hour="12"/>
- 122/141 -
</dates> <values left_color_shadow="darkgray" right_color_shadow="darkgray"> <value left="0" right="0"/> <value left="10" right="10"/> <value left="20" right="20"/> <value left="30" right="30"/> </values> <diagram type="temperature" style="percepted" name="temperatura odczuwana" color="darkcyan" values="left"> <point value="8.0"/> <point value="7.8"/> <point value="7.7"/> <point value="7.9"/> <point value="8.6"/> <point value="9.3"/> <point value="10.4"/> <point value="12.2"/> <point value="14.7"/> <point value="17.2"/> <point value="19.0"/> <point value="20.0"/> <point value="20.5"/> <point value="20.9"/> <point value="21.1"/> <point value="21.0"/> <point value="20.4"/> <point value="18.7"/> <point value="16.5"/> <point value="14.9"/> <point value="14.0"/> <point value="13.4"/> <point value="12.4"/> <point value="10.7"/> <point value="8.7"/> <point value="7.8"/> <point value="7.7"/> <point value="7.9"/> <point value="8.6"/> <point value="9.3"/> <point value="10.4"/> <point value="12.2"/> <point value="14.7"/> <point value="17.2"/> <point value="19.0"/> <point value="20.0"/> <point value="20.5"/> <point value="20.9"/> <point value="21.1"/> <point value="21.0"/> <point value="20.4"/> <point value="18.7"/> <point value="16.5"/> <point value="14.9"/> <point value="14.0"/> <point value="13.4"/> <point value="12.4"/>
- 123/141 -
<point value="10.7"/> <point value="8.7"/> <point value="7.8"/> <point value="7.7"/> <point value="7.9"/> <point value="8.6"/> <point value="9.3"/> <point value="10.4"/> <point value="12.2"/> <point value="14.7"/> <point value="17.2"/> <point value="19.0"/> <point value="20.0"/> <point value="20.5"/> </diagram> <diagram type="temperature" style="average" name="temperatura uśredniona" color="firebrick" values="right"> <point value="9.8" min_value="5.4" max_value="10.4"/> <point value="9.5" min_value="5.0" max_value="10.3"/> <point value="8.9" min_value="4.6" max_value="10.2"/> <point value="8.9" min_value="4.1" max_value="10.1"/> <point value="9.6" min_value="5.9" max_value="11.3"/> <point value="10.3" min_value="7.1" max_value="12.6"/> <point value="11.4" min_value="8.6" max_value="13.7"/> <point value="13.2" min_value="10.3" max_value="16.6"/> <point value="15.7" min_value="12.2" max_value="17.9"/> <point value="18.2" min_value="16.3" max_value="19.6"/> <point value="19.7" min_value="16.7" max_value="20.1"/> <point value="20.3" min_value="17.2" max_value="20.9"/> <point value="20.5" min_value="17.7" max_value="21.9"/> <point value="21.1" min_value="18.0" max_value="22.4"/> <point value="21.6" min_value="18.1" max_value="22.8"/> <point value="22.1" min_value="18.3" max_value="23.4"/> <point value="21.5" min_value="16.6" max_value="22.8"/> <point value="19.7" min_value="14.6" max_value="20.8"/> <point value="17.2" min_value="12.6" max_value="18.8"/> <point value="15.9" min_value="11.0" max_value="17.1"/> <point value="15.0" min_value="10.2" max_value="15.8"/> <point value="14.4" min_value="9.0" max_value="15.1"/> <point value="13.6" min_value="8.4" max_value="14.7"/> <point value="12.0" min_value="6.4" max_value="13.9"/> <point value="9.8" min_value="5.4" max_value="10.4"/> <point value="9.5" min_value="5.0" max_value="10.3"/> <point value="8.9" min_value="4.6" max_value="10.2"/> <point value="8.9" min_value="4.1" max_value="10.1"/> <point value="9.6" min_value="5.9" max_value="11.3"/> <point value="10.3" min_value="7.1" max_value="12.6"/> <point value="11.4" min_value="8.6" max_value="13.7"/> <point value="13.2" min_value="10.3" max_value="16.6"/> <point value="15.7" min_value="12.2" max_value="17.9"/> <point value="18.2" min_value="16.3" max_value="19.6"/> <point value="19.7" min_value="16.7" max_value="20.1"/> <point value="20.3" min_value="17.2" max_value="20.9"/> <point value="20.5" min_value="17.7" max_value="21.9"/> <point value="21.1" min_value="18.0" max_value="22.4"/> <point value="21.6" min_value="18.1" max_value="22.8"/>
- 124/141 -
<point value="22.1" min_value="18.3" max_value="23.4"/> <point value="21.5" min_value="16.6" max_value="22.8"/> <point value="19.7" min_value="14.6" max_value="20.8"/> <point value="17.2" min_value="12.6" max_value="18.8"/> <point value="15.9" min_value="11.0" max_value="17.1"/> <point value="15.0" min_value="10.2" max_value="15.8"/> <point value="14.4" min_value="9.0" max_value="15.1"/> <point value="13.6" min_value="8.4" max_value="14.7"/> <point value="12.0" min_value="6.4" max_value="13.9"/> <point value="9.8" min_value="5.4" max_value="10.4"/> <point value="9.5" min_value="5.0" max_value="10.3"/> <point value="8.9" min_value="4.6" max_value="10.2"/> <point value="8.9" min_value="4.1" max_value="10.1"/> <point value="9.6" min_value="5.9" max_value="11.3"/> <point value="10.3" min_value="7.1" max_value="12.6"/> <point value="11.4" min_value="8.6" max_value="13.7"/> <point value="13.2" min_value="10.3" max_value="16.6"/> <point value="15.7" min_value="12.2" max_value="17.9"/> <point value="18.2" min_value="16.3" max_value="19.6"/> <point value="19.7" min_value="16.7" max_value="20.1"/> <point value="20.3" min_value="17.2" max_value="20.9"/> <point value="20.5" min_value="17.7" max_value="21.9"/> </diagram></weather>
6.2.2. Dane XML do wykresu opadów
Dane opadów zebrane do pliku w formacie XML:
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="weather.xsl"?><weather diagram_bckg_color="lightskyblue"> <description> <title outline_color="steelblue"><![CDATA[opady - precipitation - Niederschlag (mm/h : kg/m³h)]]></title> <additional_title outline_color="darkgreen"><![CDATA[wilgotność - humidity - Feuchtigkeit (%)]]></additional_title> </description> <dates> <date day="12 X" hour="0"/> <date day="12 X" hour="3"/> <date day="12 X" hour="6"/> <date day="12 X" hour="9"/> <date day="12 X" hour="12"/> <date day="12 X" hour="15"/> <date day="12 X" hour="18"/> <date day="12 X" hour="21"/> <date day="13 X" hour="0"/> <date day="13 X" hour="3"/> <date day="13 X" hour="6"/> <date day="13 X" hour="9"/> <date day="13 X" hour="12"/> <date day="13 X" hour="15"/>
- 125/141 -
<date day="13 X" hour="18"/> <date day="13 X" hour="21"/> <date day="14 X" hour="0"/> <date day="14 X" hour="3"/> <date day="14 X" hour="6"/> <date day="14 X" hour="9"/> <date day="14 X" hour="12"/> </dates> <values left_color_shadow="gray" right_color_shadow="steelblue"> <value left="0" right="20"/> <value left="0.2" right="40"/> <value left="0.5" right="60"/> <value left="1" right="80"/> <value left="2" right="90"/> <value left="4" right="95"/> <value left="6" right="98"/> <value left="8" right="100"/> </values> <diagram type="precipitation" style="rainfall" name="deszcz ciągły"color="cadetblue" values="left"> <point value="1.1"/> <point value="2.5"/> <point value="4.9"/> <point value="5.8"/> <point value="3.6"/> <point value="1.9"/> <point value="1.6"/> <point value="0.7"/> <point value="0.3"/> <point value="0.1"/> <point value="0.02"/> <point value="0.04"/> <point value="0.10"/> <point value="0.18"/> <point value="0.09"/> <point value="0.03"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/>
- 126/141 -
<point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0.05"/> <point value="0.2"/> <point value="0.8"/> <point value="3.1"/> <point value="5.0"/> <point value="6.2"/> <point value="5.5"/> <point value="2.3"/> <point value="0.7"/> <point value="0.2"/> <point value="0.1"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> </diagram> <diagram type="precipitation" style="rainfall" name="śnieg" color="ghostwhite" values="left"> <point value="0.17"/> <point value="0.44"/> <point value="1.1"/> <point value="1.2"/> <point value="0.76"/> <point value="0.41"/> <point value="0.19"/> <point value="0.09"/> <point value="0.03"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/>
- 127/141 -
<point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> </diagram> <diagram type="precipitation" style="occasional" name="deszcz przelotny" color="darkcyan" values="left"> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/>
- 128/141 -
<point value="0"/> <point value="0.04"/> <point value="0.2"/> <point value="0.6"/> <point value="1.2"/> <point value="1"/> <point value="0.97"/> <point value="1.5"/> <point value="2.7"/> <point value="3.1"/> <point value="2.1"/> <point value="1.2"/> <point value="0.66"/> <point value="0.47"/> <point value="0.3"/> <point value="0.21"/> <point value="0.15"/> <point value="0.11"/> <point value="0.06"/> <point value="0.02"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> <point value="0"/> </diagram> <diagram type="humidity" style="line" name="wilgotność" color="darkgreen" values="right"> <point value="94.5"/> <point value="94.7"/> <point value="95.0"/> <point value="95.1"/> <point value="94.9"/> <point value="93.1"/> <point value="85.7"/> <point value="77.0"/> <point value="66.5"/> <point value="59.5"/> <point value="52.0"/> <point value="45.0"/>
- 129/141 -
<point value="40.3"/> <point value="38.7"/> <point value="37.8"/> <point value="39.9"/> <point value="47.0"/> <point value="57.0"/> <point value="66.6"/> <point value="73.0"/> <point value="78.0"/> <point value="80.8"/> <point value="82.2"/> <point value="83.3"/> <point value="84.5"/> <point value="85.5"/> <point value="86.5"/> <point value="87.0"/> <point value="86.0"/> <point value="83.5"/> <point value="78.3"/> <point value="68.0"/> <point value="55.0"/> <point value="45.5"/> <point value="38.0"/> <point value="31.0"/> <point value="27.2"/> <point value="27.0"/> <point value="28.0"/> <point value="31.8"/> <point value="42.0"/> <point value="60.0"/> <point value="67.9"/> <point value="71.7"/> <point value="74.0"/> <point value="75.5"/> <point value="77.0"/> <point value="78.0"/> <point value="79.2"/> <point value="79.5"/> <point value="79.9"/> <point value="80.1"/> <point value="79.8"/> <point value="78.9"/> <point value="78.3"/> <point value="64.0"/> <point value="52.9"/> <point value="44.9"/> <point value="38.0"/> <point value="32.0"/> <point value="27.6"/> </diagram></weather>
- 130/141 -
6.2.3. Arkusz transformacji XSLT
Kod źródłowy arkusza XSLT przeznaczonego do transformacji danych
pogodowych z formatu XML do grafiki SVG:
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:svg="http://www.w3.org/2000/svg"> <xsl:output method="xml" indent="yes" doctype-public="-//W3C//DTD SVG 1.1//EN" doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"/> <!-- XSLT STYLESHEET VARIABLES --> <xsl:variable name="viewBoxWidth" select="500"/> <xsl:variable name="viewBoxHeight" select="200"/> <xsl:variable name="picturePositionX" select="20"/> <xsl:variable name="picturePositionY" select="50"/> <xsl:variable name="pictureWidth" select="460"/> <xsl:variable name="pictureHeight" select="140"/> <!-- DOCUMENT MATCHING "WEATHER" (ROOT) ELEMENT --> <xsl:template match="/"> <svg:svg width="100%" height="100%" viewBox="0 0 {$viewBoxWidth} {$viewBoxHeight}" version="1.1"> <svg:rect x="0" y="0" width="{$viewBoxWidth}" height="{$viewBoxHeight}" style="fill:gainsboro" stroke="grey" stroke-width="1"/> <xsl:variable name="diagram_background_color" select="/weather/@diagram_bckg_color"/> <svg:rect x="{$picturePositionX}" y="{$picturePositionY}" height="{$pictureHeight}" width="{$pictureWidth}" style="fill:{$diagram_background_color}" stroke="black" stroke-width="1"/> <xsl:apply-templates/> </svg:svg> </xsl:template> <!-- DOCUMENT MATCHING "DESCRIPTION" ELEMENT --> <xsl:template match="description"> <xsl:variable name="outlineColor" select="title/@outline_color"/> <svg:text x="50%" y="8%" stroke-width="0.2" stroke="{$outlineColor}" fill="black" font-size="15" font-family="Verdana" text-anchor="middle"> <xsl:value-of select="title"/> </svg:text> <!-- TEST IF THERE'S AN ADDITIONAL HEADER TEXT --> <xsl:if test="count( /weather/description/additional_title ) > 0"> <xsl:variable name="additionalOutlineColor" select="additional_title/@outline_color"/>
- 131/141 -
<svg:text x="70%" y="15.5%" stroke-width="0.3" stroke="{$additionalOutlineColor}" fill="black" font-size="12" font-family="Verdana" text-anchor="middle"> <xsl:value-of select="additional_title"/> </svg:text> </xsl:if> </xsl:template> <!-- DOCUMENT MATCHING "DATES" ELEMENT --> <xsl:template match="dates"> <xsl:variable name="valuesCount" select="count(date) - 1"/> <xsl:variable name="diagramValueStep" select="$pictureWidth div $valuesCount"/> <xsl:for-each select="date"> <xsl:variable name="iCount" select="position() - 1"/> <xsl:variable name="valueLinePositionX" select="$picturePositionX+ $iCount * $diagramValueStep"/> <!-- DISPALY THE VALUE OF THIS POINT AT THE X AXIS --> <xsl:if test="$iCount != 0 and $iCount != last() - 1"> <svg:text x="{$valueLinePositionX}" y="{$picturePositionY - 3}" stroke-width="0.2" stroke="darkgray" fill="black" font-size="10" font-family="Verdana" text-anchor="middle"> <xsl:value-of select="@hour"/> </svg:text> </xsl:if> <!-- DISPLAY A VERTICAL LINE AT THIS VALUE'S POSITION --> <xsl:if test="$iCount > 0 and $iCount < $valuesCount"> <xsl:variable name="hour" select="@hour"/> <xsl:choose> <xsl:when test="$hour = 0"> <svg:line x1="{$valueLinePositionX}" y1="{$picturePositionY}" x2="{$valueLinePositionX}" y2="{$picturePositionY + $pictureHeight}"stroke="black" stroke-width="1" stroke-dasharray="4,3"/> </xsl:when> <xsl:otherwise> <svg:line x1="{$valueLinePositionX}" y1="{$picturePositionY}" x2="{$valueLinePositionX}" y2="{$picturePositionY + $pictureHeight}"stroke="black" stroke-width="1" stroke-dasharray="1,2"/> </xsl:otherwise> </xsl:choose> </xsl:if> <!-- IF THERE'S AN ADDITIONAL HEADER TEXT, THEN THERE'S NO NEED TO DISPLAY DAY DATES ON A DIAGRAM --> <xsl:if test="count( /weather/description/additional_title ) = 0"> <!-- DISPLAY DAY DATES AT THE TOP OF THE DIAGRAM --> <xsl:variable name="jCount1" select="position() - 1"/> <xsl:variable name="jCount2" select="position()"/> <xsl:variable name="leftDate" select="/weather/dates/date[$jCount1]/@day"/> <xsl:variable name="rightDate" select="/weather/dates/date[$jCount2]/@day"/> <xsl:if test="$leftDate != $rightDate or position() = 1 or position() = last()"> <xsl:variable name="endCount" select="position()"/>
- 132/141 -
<xsl:if test="$jCount1 != 0"> <!-- FIND INDEX OF THE VERY FIRST ELEMENT CONTAINING "/weather/dates/date[???]/@day" ATTRIBUTE'S VALUE EQUAL TO "$leftDate" --> <xsl:for-each select="/weather/dates/date"> <xsl:variable name="previousElementIndex" select="position() - 1"/> <xsl:variable name="previousElementValue" select="/weather/dates/date[$previousElementIndex]/@day"/> <xsl:if test="@day = $leftDate and ( $previousElementValue !=$leftDate or $previousElementIndex = 0)"> <!-- THIS SECTION SHOULD DISPLAY INTERVALS LIKE: 1-8, 8-16, 16-20, ETC. --> <!-- <xsl:value-of select="position()"/>-<xsl:value-of select="$endCount"/> --> <!-- CALCULATE LOCATION OF THE DATE ON THE PICTURE --> <xsl:variable name="dayDatePositionX" select="$picturePositionX + $diagramValueStep * ( ( $endCount - position() ) div 2 + position() - 1 )"/> <!-- DISPLAY DAY DATE IN THE COUNTED POSITION --> <svg:text x="{$dayDatePositionX}" y="{$picturePositionY - 18}" stroke-width="0.1" stroke="yellowgreen" fill="black" font-size="12" font-family="Verdana" text-anchor="middle"> - <xsl:value-of select="$leftDate"/> - </svg:text> </xsl:if> </xsl:for-each> </xsl:if> </xsl:if> </xsl:if> </xsl:for-each> </xsl:template> <!-- DOCUMENT MATCHING "VALUES" ELEMENT --> <xsl:template match="values"> <xsl:variable name="valuesCount" select="count(value) - 1"/> <xsl:variable name="diagramValueStep" select="$pictureHeight div $valuesCount"/> <xsl:variable name="leftColorShadow" select="@left_color_shadow"/> <xsl:variable name="rightColorShadow" select="@right_color_shadow"/> <xsl:for-each select="value"> <xsl:variable name="iCount" select="position() - 1"/> <xsl:variable name="valueLinePositionY" select="$pictureHeight + $picturePositionY - $iCount * $diagramValueStep"/> <!-- DISPALY THE VALUE OF THIS POINT AT THE Y AXIS ON THE LEFT SIDE OF A DIAGRAM--> <svg:text x="{$picturePositionX - 2}" y="{$valueLinePositionY}" stroke-width="0.2" stroke="{$leftColorShadow}" fill="black" font-size="10" font-family="Verdana" text-anchor="end"> <xsl:value-of select="@left"/> </svg:text> <!-- DISPALY THE VALUE OF THIS POINT AT THE Y AXIS ON THE RIGHT SIDE OF A DIAGRAM--> <svg:text x="{$picturePositionX + $pictureWidth + 2}"
- 133/141 -
y="{$valueLinePositionY}" stroke-width="0.2" stroke="{$rightColorShadow}" fill="black" font-size="10" font-family="Verdana" text-anchor="start"> <xsl:value-of select="@right"/> </svg:text> <!-- DISPLAY A HORIZONTAL LINE AT THIS VALUE'S POSITION --> <xsl:if test="$iCount > 0 and $iCount < $valuesCount"> <svg:line x1="{$picturePositionX}" y1="{$valueLinePositionY}" x2="{$picturePositionX + $pictureWidth}" y2="{$valueLinePositionY}" stroke="black" stroke-width="1" stroke-dasharray="1,2"/> </xsl:if> </xsl:for-each> </xsl:template> <!-- DOCUMENT MATCHING "DIAGRAM" ELEMENT --> <xsl:template match="diagram"> <xsl:variable name="valuesDataSide" select="@values"/> <xsl:variable name="valuesRange" select="/weather/values/value[ count(/weather/values/value) ]/@left - /weather/values/value[1]/@left"/> <xsl:variable name="valueStep" select="$pictureHeight div $valuesRange"/> <xsl:variable name="argumentStep" select="$pictureWidth div ( count(point) - 1 )"/> <xsl:variable name="diagramType" select="@type"/> <xsl:variable name="diagramStyle" select="@style"/> <xsl:variable name="diagramColor" select="@color"/> <xsl:for-each select="point"> <xsl:variable name="x1" select="$picturePositionX + ( position() - 1 ) * $argumentStep"/> <xsl:if test="position() != last()"> <xsl:variable name="x2" select="$picturePositionX + ( position()) * $argumentStep"/> <xsl:variable name="y1" select="$picturePositionY + $pictureHeight - @value * $valueStep"/> <xsl:variable name="nextValue" select="position() + 1"/> <xsl:variable name="y2" select="$picturePositionY + $pictureHeight - ( ../point[ $nextValue ]/@value ) * $valueStep"/> <!-- *********************************** --> <!-- DRAWING DIAGRAM OF TEMPERATURE TYPE --> <!-- *********************************** --> <xsl:if test="$diagramType = 'temperature'"> <!-- DIAGRAM CONNECTING PERCEPTED AND AVERAGE TEMPERATURE POINTS --> <svg:line x1="{$x1}" y1="{$y1}" x2="{$x2}" y2="{$y2}" stroke="{$diagramColor}" stroke-width="2" stroke-dasharray="1,0" stroke-linecap="round"/> <xsl:if test="$diagramStyle = 'average'"> <!-- DIAGRAM CONNECTING TEMPERATURE'S MIN AND MAX VALUES --> <xsl:if test="position() != 1"> <xsl:variable name="y3" select="$picturePositionY + $pictureHeight - @min_value * $valueStep"/> <xsl:variable name="y4" select="$picturePositionY + $pictureHeight - @max_value * $valueStep"/> <svg:line x1="{$x1}" y1="{$y3}" x2="{$x1}" y2="{$y4}"
- 134/141 -
stroke="{$diagramColor}" stroke-width="0.5" stroke-dasharray="1,0" stroke-linecap="square"/> </xsl:if> </xsl:if> </xsl:if> <!-- ******************************** --> <!-- DRAWING DIAGRAM OF HUMIDITY TYPE --> <!-- ******************************** --> <xsl:variable name="thisPointValue" select="@value"/> <xsl:variable name="nextValuePosition" select="position() + 1"/> <xsl:variable name="nextPointValue" select="../point[$nextValuePosition]/@value"/> <xsl:if test="$diagramType = 'humidity' or $diagramType = 'precipitation'"> <!-- 1. CALCULATE NEW Y VALUE ON THE PICTURE --> <!-- a) GET PIXEL RANGE BETWEEN TWO HORIZONTAL LINES --> <xsl:variable name="pixelRange" select="$pictureHeight div ( count(/weather/values/value) - 1 )"/> <!-- b) GO THROUGH ALL THE POSSIBLE VALUE RANGES AND SELECT THEONE TO WHICH THE CURRENT VALUE APPLIES --> <xsl:variable name="valuesSide" select="../@values"/> <xsl:for-each select="/weather/values/value"> <xsl:variable name="leftBottomRange" select="@left"/> <xsl:variable name="rightBottomRange" select="@right"/> <xsl:variable name="nextPosition" select="position() + 1"/> <xsl:variable name="leftTopRange" select="../value[$nextPosition]/@left"/> <xsl:variable name="rightTopRange" select="../value[$nextPosition]/@right"/> <!-- c) CALCULATE DIAGRAM Y VALUE FOR BOTH CASES: LEFT AND RIGHT Y AXIS --> <xsl:if test="$valuesSide = 'right' and $rightBottomRange <= $thisPointValue and $rightTopRange >= $thisPointValue"> <!-- d) CALCULATING VALUES FROM THE RIGHT'S AXIS RANGE --> <xsl:variable name="singleValueStep" select="$pixelRange div ( $rightTopRange - $rightBottomRange )"/> <xsl:variable name="additionalValueSubtrahend" select="$thisPointValue - $rightBottomRange"/> <xsl:variable name="diagramThisPositionYRight" select="$pictureHeight + $picturePositionY - ( ( position() - 1 ) * $pixelRange ) - $additionalValueSubtrahend * $singleValueStep"/> <!-- NOW WHEN WE'VE GOT THE DIAGRAM'S Y POSITION OF OUR VALUE, WE HAVE TO CALCULATE THE POSITION OF THE NEXT POINT TO CONNECT IT TO --> <xsl:for-each select="/weather/values/value"> <xsl:variable name="nextBottomRange" select="@right"/> <xsl:variable name="nextNextPosition" select="position() + 1"/> <xsl:variable name="nextTopRange" select="../value[$nextNextPosition]/@right"/> <xsl:if test="$nextBottomRange <= $nextPointValue and $nextTopRange >= $nextPointValue"> <xsl:variable name="nextSingleValueStep" select="$pixelRange div ( $nextTopRange - $nextBottomRange )"/> <xsl:variable name="nextAdditionalValueSubtrahend" select="$nextPointValue - $nextBottomRange"/>
- 135/141 -
<xsl:variable name="diagramNextPositionYRight" select="$pictureHeight + $picturePositionY - ( ( position() - 1 ) * $pixelRange ) - $nextAdditionalValueSubtrahend * $nextSingleValueStep"/> <!-- DIAGRAM STYLE: RAINFALL --> <xsl:if test="$diagramStyle = 'rainfall'"> <svg:rect x="{$x1 + 1}" y="{$diagramThisPositionYRight}" width="{$x2 - $x1}" height="{$pictureHeight + $picturePositionY - $diagramThisPositionYRight - 1}" fill="{$diagramColor}" stroke="{$diagramColor}" stroke-width="1"/> </xsl:if> <!-- DIAGRAM STYLE: LINE --> <xsl:if test="$diagramStyle = 'line'"> <svg:line x1="{$x1}" y1="{$diagramThisPositionYRight}" x2="{$x2}" y2="{$diagramNextPositionYRight}" stroke="{$diagramColor}" stroke-width="2" stroke-dasharray="1,0" stroke-linecap="round"/> </xsl:if> </xsl:if> </xsl:for-each> </xsl:if> <xsl:if test="$valuesSide = 'left' and $leftBottomRange <= $thisPointValue and $leftTopRange >= $thisPointValue"> <!-- e) CALCULATING VALUES FROM THE LEFT'S AXIS RANGE --> <xsl:variable name="singleValueStep" select="$pixelRange div ( $leftTopRange - $leftBottomRange )"/> <xsl:variable name="additionalValueSubtrahend" select="$thisPointValue - $leftBottomRange"/> <xsl:variable name="diagramThisPositionYLeft" select="$pictureHeight + $picturePositionY - ( ( position() - 1 ) * $pixelRange ) - $additionalValueSubtrahend * $singleValueStep"/> <!-- NOW WHEN WE'VE GOT THE DIAGRAM'S Y POSITION OF OUR VALUE, WE HAVE TO CALCULATE THE POSITION OF THE NEXT POINT TO CONNECT IT TO --> <xsl:for-each select="/weather/values/value"> <xsl:variable name="nextBottomRange" select="@left"/> <xsl:variable name="nextNextPosition" select="position() + 1"/> <xsl:variable name="nextTopRange" select="../value[$nextNextPosition]/@left"/> <xsl:if test="$nextBottomRange <= $nextPointValue and $nextTopRange >= $nextPointValue"> <xsl:variable name="nextSingleValueStep" select="$pixelRange div ( $nextTopRange - $nextBottomRange )"/> <xsl:variable name="nextAdditionalValueSubtrahend" select="$nextPointValue - $nextBottomRange"/> <xsl:variable name="diagramNextPositionYLeft" select="$pictureHeight + $picturePositionY - ( ( position() - 1 ) * $pixelRange ) - $nextAdditionalValueSubtrahend * $nextSingleValueStep"/> <!-- DIAGRAM STYLE: RAINFALL --> <xsl:if test="$diagramStyle = 'rainfall'"> <svg:rect x="{$x1 + 1}" y="{$diagramThisPositionYLeft}" width="{$x2 - $x1}" height="{$pictureHeight + $picturePositionY - $diagramThisPositionYLeft - 1}" fill="{$diagramColor}" stroke="{$diagramColor}" stroke-width="1"/>
- 136/141 -
</xsl:if> <!-- DIAGRAM STYLE: LINE --> <xsl:if test="$diagramStyle = 'line'"> <svg:line x1="{$x1}" y1="{$diagramThisPositionYLeft}" x2="{$x2}" y2="{$diagramNextPositionYLeft}" stroke="{$diagramColor}"stroke-width="2" stroke-dasharray="1,0" stroke-linecap="round"/> </xsl:if> <!-- DIAGRAM STYLE: OCCASIONAL --> <xsl:if test="$diagramStyle = 'occasional' and $thisPointValue != 0"> <svg:line x1="{$x1 + ($x2 - $x1) div 2}" y1="{$diagramThisPositionYLeft}" x2="{$x1 + ($x2 - $x1) div 2}" y2="{$picturePositionY + $pictureHeight - 0.5}" stroke="{$diagramColor}" stroke-width="1" stroke-dasharray="1,0" stroke-linecap="butt"/> </xsl:if> </xsl:if> </xsl:for-each> </xsl:if> </xsl:for-each> </xsl:if> </xsl:if> </xsl:for-each> </xsl:template></xsl:stylesheet>
- 137/141 -
7. Bibliografia
[1] World Wide Web Consortium (W3C) MembersStrona Internetowa: http://www.w3.org/Consortium/Member/List/
[2] Tim Berners-Lee – BiografiaStrona Internetowa: http://www.w3.org/People/Berners-Lee/
[3] About the World Wide Web Consortium (W3C)Strona Internetowa: http://www.w3.org/Consortium/
[4] Scalable Vector Graphics (SVG): XML Graphics for the WebStrona Internetowa: http://www.w3.org/Graphics/SVG/
[5] Scalable Vector Graphics (SVG) 1.0 Specification (W3C Recommendation)Strona Internetowa: http://www.w3.org/TR/SVG10/
[6] About SVG: 2D Graphics in XMLStrona Internetowa: http://www.w3.org/Graphics/SVG/About.html
[7] Scalable Vector Graphics (SVG) Full 1.2 Specification: W3C Working DraftStrona Internetowa: http://www.w3.org/TR/SVG12/
[8] Scalable Vector Graphics (SVG): Group MembersStrona Internetowa: http://www.w3.org/Graphics/SVG/Membership
- 138/141 -
[9] W3C Scalable Vector Graphics (SVG) – HistoryStrona Internetowa: http://www.w3.org/Graphics/SVG/History
[10] Scalable Vector Graphics Tiny 1.2 Specification: W3C CandidateStrona Internetowa: http://www.w3.org/TR/SVGMobile12/
[11] Prognoza Pogody ICMStrona Internetowa: http://meteo.icm.edu.pl/
[12] Scalable Vector Graphics (SVG) 1.1 Specification: ConceptsStrona Internetowa: http://www.w3.org/TR/SVG11/concepts.html
[13] Mobile SVG Profiles: SVG Tiny and SVG Basic: W3C RecommendationStrona Internetowa: http://www.w3.org/TR/SVGMobile/
[14] SVG.org: Shipping and Announced SVG PhonesStrona Internetowa: http://svg.org/special/svg_phones/
[15] Scalable Vector Graphics (SVG) 1.1 Specification: W3C RecommendationStrona Internetowa: http://www.w3.org/TR/SVG11/
[16] SVG 1.1/1.2/2.0 Requirements: W3C Working DraftStrona Internetowa: http://www.w3.org/TR/SVG2Reqs/
8. Słownik trudniejszych pojęć
cieniowanie Gourauda (ang. Gouraud shading) – jedna z metod symulowania
zmiany jasności obiektów w grafice komputerowej poprzez odpowiednie
cieniowanie kolorów na ich powierzchni, opracowana przez francuskiego
matematyka Henri Gourauda
draft (ang. szkic, projekt) – propozycja nowej wersji standardu
enkapsulacja (ang. encapsulation) – w programowaniu obiektowym ukrywanie
danych składowych klasy, tak aby były one niedostępne dla użytkownika
funkcja turbulencji Perlina (ang. Perlin Noise) – pseudolosowa funkcja szeroko
stosowana w grafice komputerowej, używana do zwiększenia realizmu różnych
obiektów (np. chmur), opracowana przez Kena Perlina, profesora New York
University
- 139/141 -
hiperłącze (ang. hyperlink) – odnośnik do innego dokumentu lub do innego
miejsca w bieżącym dokumencie elektronicznym
kanał przezroczystości (ang. alpha channel) – dodatkowa informacja jaką może
nieść ze sobą każdy piksel, używana do generowania przezroczystości obiektów
przez ich wkomponowanie w tło rysunku
kierunkowe źródło światła (ang. distant light) – jedna ze składowych
modelujących oświetlenie w grafice komputerowej, w której zakłada się
równoległość promieni świetlnych, używana do modelowania odległych obiektów
(np. światła słonecznego)
model oświetlenia Phonga – jeden z modeli służących do symulowania zmiany
odbicia światła od różnych obiektów w grafice komputerowej, opracowany przez
Phong Bui-Tuonga
morfing (ang. morphing) – płynne przekształcanie jednego obrazu w inny
progowanie (ang. thresholding) – efekt graficzny polegający podzieleniu obrazu
na wiele regionów, z których część składa się z pikseli mających jasność większą
niż określona wartość progowa, a część z pikseli o jasności mniejszej, które
nazywane są pikselami tła (ang. background pixels)
przestrzenie nazw XML (ang. XML Namespaces) – mechanizm opracowany
przez W3C umożliwiający stosowanie w jednym dokumencie znaczników z różnych
języków XML
punktowe źródło światła (ang. point light) – jedna ze składowych modelujących
oświetlenie w grafice komputerowej, w której zakłada się pochodzenie promieni
świetlnych z pojedynczego punktu, używana do modelowania bliskich obiektów
(np. światła żarówki)
renderowanie (ang. rendering) – w grafice komputerowej oznacza proces
generowania gotowego obrazu w oparciu o dostarczone dane
rozmycie gaussowskie (ang. Gaussian blur) – szeroko stosowany efekt
- 140/141 -
graficzny, służący do zmniejszenia liczby szczegółów na rysunku poprzez jego
rozmycie
- 141/141 -