Tworzenie elementów HTML
JavaScript posiada kilka funkcji które pozwalają na łatwiejsze modyfikowanie drzewa DOM tworzonego przez HTML.
Dowolny element HTML możemy stworzyć wykorzystując document.createElement(elementName)
, więc żeby stworzyć
pusty <div>
: document.createElement('div')
.
Do tak stworzonego elementu możemy przypisać tekst wykorzystując znany już innerText
. Elementy w HTML są w sobie zagnieżdżone, takie zagnieżdżanie w JavaScript tworzymy wykorzystując funkcję appendChild
na istniejącym już elemencie.
Czyli, jeśli chcemy stworzyć fragment drzewa DOM z nagłówkiem strony i dodać go do elementu body
wykonamy:
Elementy drzewa DOM można ostylować za pomocą pliku CSS i klas/id, lub wpisywać CSS „inline” przypisując go do elementu. Jeśli chcemy zmienić kolor i czcionkę naszego nagłówka możemy wykorzystać klucz style
on stworzonym przez nas elemencie:
var header = document.createElement("h1");
header.innerText = "Wizualizacja danych";
header.style.fontFamily = "sans-serif";
header.style.color = "#bbb";
document.body.appendChild(header);
Wizualizacja za pomocą elementów HTML
Do zbudowania prostego wykresu możemy wykorzystać odpowiednio ostylowane elementy <div>
.
Zaczniemy od przygotowania prostej tablicy danych opisującej średnią temperaturę w Polsce dla każdego miesiąca z 2015 roku, stworzymy też tablicę miesięcy:
var avgTemperature = [ 0.94, 0.95, 5.29, 7.97, 12.70, 16.26, 19.19, 20.63, 14.98, 8.13, 5.51, 4.27 ];
var monthNames = [ "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień" ];
Zaczniemy od stworzenia elementu <div>
dla każdego
miesiąca:
for (var i = 0; i < avgTemperature.length; i = i + 1) {
var div = document.createElement("div");
div.innerText = monthNames[i] + ": " + avgTemperature[i];
document.body.appendChild(div);
}
Możemy teraz wykorzystać kolejny element div który ostylujemy podając
stałą wysokość, oraz szerokość zależną od danych:
Najważniejszy fragment tego kodu to:
bg.style.width = avgTemperature[i] * 20 + "px";
bg.style.height = "2px";
Ustalamy wysokość elementu na dwa piksele, a jego szerokość na średnią temperaturę z danego miesiąca (przeskalowaną żeby była lepiej widoczna), również w pikselach (należy pamiętać o dodaniu"px"
).
Wizualizacja danych z API
Możemy teraz połączyć dane pobrane z API oraz powyższą technikę wizualizacji.
Najpierw musimy tak przefiltrować dane, żeby wyciągnąć z nich to co nas interesuje – załóżmy że chcemy zwizualizować liczbę osób żyjących w Polsce na przestrzeni lat.
Dane z naszego pliku JSON to tablica obiektów, każdy z tych obiektów posiada pole meta
oraz data
. Pole meta
opisuje jakie informacje znajdują się w polu data
. My poszukujemy takiego pola meta
gdzie
wartość n1
oraz n2
jest równa "ogółem"
, zacznijmy od stworzenia funkcji filterData
która ułatwi nam pracę z danymi z API:
function filterData(apiData) {
var dataPoints;
for (var i = 0; i < apiData.length; i = i + 1) {
if (apiData[i].meta.n1 == "ogółem" && apiData[i].meta.n2 == "ogółem") {
dataPoints = apiData[i].data.results[0].values;
}
}
return dataPoints;
}
(&&
oznacza logiczne „i”, ||
oznacza
logiczne „lub”)
Powyższą funkcję możemy połączyć z przykładem pobierającym i
wyświetlającym dane, żeby upewnić się czy wszystko działa:
Mamy teraz w wyniku tablicę obiektów z polami year
oraz
val
. Wartości val
są bardzo duże, więc
znajdźmy maksimum i przeskalujmy je:
var max = 0;
for (var i = 0; i < filtered.length; i = i + 1) {
if (filtered[i].val > max) {
max = filtered[i].val;
}
}
Powyższy prosty algorytm przechodzi po wszystkich wartościach
val
i jeśli napotka taką, która jest większa od obecnego
max
to zapamiętuje max
jako szukaną wartość.
Łącząc powyższy kod z wcześniejszym, wizualizującym pogodę w Polsce,
otrzymamy:
Taki wykres za dużo nie pokazuje, możemy więc znaleźć jeszcze
najmniejszą wartość i przeskalować go od najmniejszej do największej:
Wykorzystywanie bibliotek JavaScript
Biblioteka to nic innego jak zestaw funkcji i obiektów napisanych już
przez kogoś, które możemy użyć w swoim projekcie.
Obecnie istnieje wiele sposobów na dodanie biblioteki do kodu
JavaScript nad którym pracujemy, ale najprostsza metoda to wciąż tag
<script>
w HTML:
<script src="http://some/library.js"></script>
Wizualizacja wykorzystując Chart.js
Przygodę z bibliotekami zaczniemy od
Chart.js
– jednej
z najprostszych w obsłudze bibliotek do wizualizacji danych.
Biblioteka ta działa w oparciu o element <canvas>
,
musimy go stworzyć, dodać do document.body
oraz
udostępnić context
bibliotece Chart.js
.
context
to obiekt który udostępnia funkcje rysujące
działające w obrębie danego elementu <canvas>
.
var canvas = document.createElement("canvas");
var context = canvas.getContext("2d");
document.body.appendChild(canvas);
Tak przygotowany <canvas>
połączymy z pierwszym
przykładem o średnich temperaturach w Polsce:
(W zakładce HTML znajduje się tag <script>
ładujący
bibliotekę Chart.js
)
Jak widać konfiguracja wykresu odbywa się przez obiekt:
{
type: "bar",
data: {
labels: monthNames,
datasets: [
{
label: "Średnia temperatura w Polsce (2017)",
data: avgTemperature
}
]
}
}
type
– opisuje typ wykresu ("bar"
,
"line"
,"radar"
,"pie"
,
"scatter"
)data
– obiekt opisujący jakie dane znajdą się na
wykresielabels
– tablica etykietek (powinno ich być tyle
ile danych)datasets
– tablica zestawów danych jakie
wizualizujemy, w naszym wypadku mamy tylko jedną serię danychlabel
– etykietka serii danychdata
– tablica z danymi
Możemy teraz narysować wykres z dwoma seriami, np. porównać liczbę
kobiet i mężczyzn na przestrzeni lat.
Uogólnijmy funkcję filtrującą, tak że można jej przekazać parametry
n1
oraz n2
:
function filterData(apiData, n1, n2) {
var dataPoints;
for (var i = 0; i < apiData.length; i = i + 1) {
if (apiData[i].meta.n1 == n1 && apiData[i].meta.n2 == n2) {
dataPoints = apiData[i].data.results[0].values;
}
}
return dataPoints;
}
Możemy teraz połączyć Chart.js
z danymi z pliku JSON:
Wizualizacja wykorzystując p5.js
Chart.js
pozwala na szybkie stworzenie dobrze
wyglądającej wizualizacji, kosztem dość niskiego stopnia
konfigurowalności.
Druga biblioteka którą pokrótce poznamy to
p5.js, adaptacja znanego języka
Processing działająca w
JavaScript.
Tak samo jak w Chart.js
musimy zacząć od załączenia
biblioteki w kodzie HTML naszego projektu:
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>
p5.js
wymaga stworzenia przez nas dwóch funkcji:
setup
oraz draw
.
setup
uruchamiana jest tylko raz, a
draw
uruchamia się około 60 razy na sekundę. Tak jak
wskazują nazwy, setup
odpowiada za ustawienia naszego
szkicu, a draw
za rysowanie rzeczy na ekranie.
Podstawowy skrypt wygląda następująco:
function setup() {
createCanvas(600, 600);
}
function draw() {
background(200);
}
createCanvas
tworzy element
<canvas>
i dodaje go do drzewa DOM, więc nie
musimy tego robić ręcznie tak jak wChart.js
, argumenty
tej funkcji to wysokość i szerokośćcanvas
background
wypełniacanvas
jednolitym
kolorem, można podać jako argument stopień szarości
(0-255
), lub trzy wartości opisujące kolor w RGB (każda
z nich0-255
)
Zbudujemy teraz wizualizację która na osi y pokazuje przedziały
wiekowe, na osi x kolejne lata, a dane przedstawia jako okręgi
skalowane ilością ludzi oraz kolorowane bazując na płci.
Żeby pobrane dane były „widoczne” wewnątrz funkcji
draw
stworzymy globalną zmienną data
która
zostanie podmienion po wykonaniu fetch
:
var data = [];
fetch("https://cors-anywhere.herokuapp.com/http://satyrium.pl:8080/work/media-3.0-datavis-course/data.json")
.then(function(response) {
return response.json();
})
.then(function(result) {
data = result;
});
function setup() {
createCanvas(600, 600);
}
function draw() {
background(240);
// data będzie pustą tabelą, a po chwili wynikami z fetch, gdy dane zostaną pobrane
}
Wybierzmy z danych kilka przedziałów wiekowych:
var ageRanges = [ "0-4", "5-9", "10-14", "15-19", "20-24", "25-29", "30-34", "35-39", "40-44", "45-49", "50-54", "55-59", "60-64", "65-69", "70-74", "75-79", "80-84" ];
Wyciągnijmy też z danych dostępne lata:
var years = [];
if (data.length > 0) {
for (var i = 0; i < data[0].data.results[0].values.length; i = i + 1) {
years.push(data[0].data.results[0].values[i].year);
}
}
Możemy teraz połączyć zakresy wieków i lata i wyciągnąć interesujące
nas dane wykorzystując wcześniejszą funkcję filterData
:
for (var i = 0; i < ageRanges.length; i = i + 1) {
// ageRanges[i] to dany przedział wiekowy - wyszukujemy kobiety i mężczyzn osobno
var women = filterData(data, ageRanges[i], "kobiety");
var men = filterData(data, ageRanges[i], "mężczyźni");
// przechodzimy po wszystkich latach
for (var j = 0; j < years.length; j = j + 1) {
// "wyciągamy" dane gdzie rok jest naszym poszukiwanym rokiem
var womenValue = 0;
var menValue = 0;
for (var k = 0; k < women.length; k = k + 1) {
if (women[k].year == years[j]) {
womenValue = women[k].val;
}
if (men[k].year == years[j]) {
menValue = men[k].val;
}
}
// przeskalowane położenie na osi x i y - dobrane "eksperymentalnie"
var x = i * 35 + 15;
var y = j * 25 + 20;
// przeskalowanie wartości - dobrane "eksperymentalnie" - można by wykorzystać min i max z wcześniejszych przykładów
var rWomen = womenValue / 100000;
var rMen = menValue / 100000;
fill(255, 0, 0, 150);
ellipse(x, y, rWomen, rWomen);
fill(0, 0, 255, 150);
// rysujemy mężczyzn "przesuniętych" o 10 pikseli, żeby obie wartości były widoczne
ellipse(x + 10, y, rMen, rMen);
}
}
Dwie nowe funkcje należące do p5.js
to:
fill
– ustala kolor wypełnienia, można podać skalę
szarości, RGB lub RGBA (gdzie „A” to „alpha” czyli stopień
przeźroczystości)ellipse
– rysuje elipsę w podanym miejscu
x
/y
, dwie kolejne wartości to promień w
osix
oraz w osiy
– jeśli promienie są
sobie równe, to rysujemy okrąg
Finalny kod poniżej:
Jak widać p5.js
wymaga nieco bardziej „niskopoziomowego”
myślenia do stworzenia wizualizacji, ale pozwala przez to na
zbudowanie zupełnie niestandardowych grafik.
Quiz: