D3Część NR.1Wizualizacja danych. Kiedyś do pracy w trakcie marszu śmierci musiałem szybko napisać kod który by rysował wykres. Skorzystałem wtedy z biblioteki JavaScript Highchart.js. Obecnie sobie uświadamiań, że te głupie wykresy które napisałem spełniają bardzo ważną rolę biznesową dla klientów. Postanowiłem więc nauczyć się bardziej zawansowanej biblioteki stworzeń do tworzenia grafik i wykresów.

D3 pozwala na manipulowanie elementami na stronie HTML w kontekście zbioru danych. Te elementy mogą być tagami HTML i grafikami SVG lub Canvas.

D3 nie jest gigantycznym frameworkiem, które ma gotowe metody do tworzenia wykresów. D3 jest tylko narzędziem pomocniczym.

Oznacza to, że po nauce D3  jedynie co nas ogranicza do działania to nasza własna pomysłowość i wyobraźnia.

Jestem programistą .NET więc korzystam z narzędzia Visual Studio 2013. W tym cyklu będę operował tylko na plikach JavaScript więc nie trzeba posiadać żadnych szczególnych narzędzi by śledzi moje działania.

Ten tutorial mógłby być równie dobrze wykonany przy pomocy edytora Submile Text 2.

Jeśli jesteś szczęśliwym posiadaczem Visual Studio dodanie biblioteki D3 do projektu jest bardzo łatwe. Wystarczy skorzystać z menadżera paczek NuGet.

NuGet

W przeciwnym wypadku musisz pobrać bibliotekę D3 ze strony http://d3js.org  lub GitHub-a https://github.com/mbostock/d3

Mój układ projektu wygląda następująco. W folderze Scripts znajduję się biblioteka D3 wraz z jej wersją spłaszczoną bez białych znaków (min). W projekcie mam plik Index.html na którym będę operował.

Układ projektu

D3 nie jest zależne od innych bibliotek JavaScript. Nie ma tutaj jQuery.

Aby go użyć wystarczy do strony dodać odwołanie do pliku JavaScript tej biblioteki.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Zabawa z D3</title>
    <script src="Scripts/d3.v3.min.js"></script>
</head>
<body>

</body>
</html>

W ciele strony dodajemy skrypt który będzie manipulował elementami graficznymi strony.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Zabawa z D3</title>
    <script src="Scripts/d3.v3.min.js"></script>
</head>
<body>
    <script type="text/javascript">
        // Kod D3 idzie tutaj
    </script>
</body>
</html>

Wszystko jest już gotowe. Stworzymy więc prosty wykres.

Hello i pobieranie danych z pliku CSV

Poniższy kod doda do strony element Div z napisem Hello.

Metoda “select” wybiera jeden element znajdujący się na stronie. W przypadku gdy będziemy operować na większej ilości elementów korzystamy z metody “selectAll”.

Metoda “append” dodaje element DOM do elementu, który został określony wcześniej.

Metoda “text” umieszcza zawartość tekstową wewnątrz elementu, który został określony wcześniej czyli w naszym nowym div-ie.

<script type="text/javascript">
    d3.select("body").append("div").text("Hello");
</script>

Łańcuchy funkcji są zalecane  i bardzo skracają kod. Powyższy kod bez tego mechanizmu wyglądał by tak.

var b = d3.select("body")
var div = b.append("div")
div.text("Hello");

Wiemy jak już dodawać elementy używając D3. Bez danych jednak nie ma żadnej zabawy.

D3 ma możliwość odczytu danych z plików w różnych formatach n.p w formacie CSV. Pierwsza linijka definiuje nazwy informacji a dalsze linijki definiują same informację.

name,age
Cezary,25
Franek,11
Bartek,29
Kamil,29
Paweł,31

Zakładając, że masz plik CSV w projekcie możesz w taki sposób wyświetlić informację w nim zawarte.

d3.csv("humans.csv", function (data) {

    console.log(data);

    data.forEach(function (entry) {
        d3.select("body").append("div").text(entry.name);
    });
});

Dane w JavaScript są oczywiście przechowywane jako obiekty.  W tym wypadku obiekt “data” jest tablicą przechowującą obiekty który zawiera wiek i imię osoby.

image

W tym przykładzie użyłem JavaScriptowej pętli forEach aby wybrać elementy z tablicy, ale jak zaraz się sam przekonasz nie potrzebujemy takiego kodu.

Dane w formacie JSON

Otwierania lokalnych plików jest fajne, ale w prawdziwym życiu prawdopodobnie będziesz działać na odpowiedziach serwera który będzie zwracać dane w formacie JSON. JSON jest naturalną notacją dla JavaScriptu.

Nie jest to kurs o WEB API więc dla ułatwienia przykładów będę umieszczał dane do  zmiennej globalnej. Nie jest to do końca dobry pomysł, ale ta praktyka wystarczy do naszych przykładów.

var json = [
    { "name": "Cezary", "age": 25 },
    { "name": "Franek", "age": 11 },
    { "name": "Bartek", "age": 29 },
];

Nawias kwadratowy tworzy tablice elementu. Definicje obiektów znajdują się w nawiasach klamrowych.

var json = [
    { "name": "Cezary", "age": 25 },
    { "name": "Franek", "age": 11 },
    { "name": "Bartek", "age": 29 },
];

d3.csv("humans.csv", function (data) {

    data.forEach(function (entry) {
        d3.select("body").append("div").text(entry.name);
    });

    json.forEach(function (entry) {
        d3.select("body").append("p").text(entry.name);
    });

});

Przykład z użyciem JSON wygląda tak samo jak w poprzednim przykładzie. Czas jednak na gwałtowny ruch do przodu.

Po pierwsze nie potrzebujemy pętli forEach by operować na kolekcji obiektów. Po drugie skoro już korzystamy z łańcuchu metod dla czytelności kodu lepiej oddzielać każde wywołanie funkcji nową linijką. JavaScript ignoruje w takim wypadku białe znaki i znaki nowej linii.

Poniższy kod stworzy listę elementów.

var json = [
    { "name": "Cezary", "age": 25 },
    { "name": "Franek", "age": 11 },
    { "name": "Bartek", "age": 29 },
];

function createAList(data) {
    d3.select("body")
    .append("ul")
    .selectAll("li")
    .data(data)
    .enter()
    .append("li")
    .text(function (d) {
        return d.name + ": " + d.age;
    })
}

createAList(json);

Metoda “select” i “append” robią to samo co wcześniej.

Metoda “selectAll” zaznacza wszystkie elementy “li” znajdujących się w dokumencie. Żaden taki element  jeszcze  nie istnieje. Skoro taki element nie istnieje na tym poziomie bez dalszych wołań funkcji normalnie zostałby zwrócony pusty element. Pomyśl o tym jednak jak o reprezentacji punktu w liście, który zaraz się pojawi. Zostanie on stworzy przez następne funkcję. Poprzednie funkcje “select” i “append”   opakują elementy lielementem ul.

Metoda data(data) wykonuje pętlę po tablic umieszczonej wewnątrz funkcji. Zmienna json ma 3 elementy więc każda następna funkcja wywoła się 3 razy.

Aby stworzyć nowy element musimy użyć metody enter. Ta metoda patrzy na  nasze obecne zaznaczenie  i dane jakie posiadamy. Na tej podstawie D3 przygotowuje się utworzenia elementów. Funkcja ta zwraca miejsce w który element ma się pojawić.

Metoda appenddodaje nowy  element listy w miejscu ustalonym przez poprzednią funkcjęenter.

Wewnątrz elementu li tworzymy zawartość tekstową. Może to być zwykły napis, ale wewnątrz tej funkcji możemy dodać naszą własną funkcję, która będzie zwracać pożądany przez nas napis.

W tym wypadku imię plus wiek.

Funkcja createAListjest już gotowa pozostaje mi ją tylko uruchomić.

image

Funkcja ta wydrukowała nam prostą listę wypunktowaną.

Potęga D3 nie kończy się tutaj. Możemy operować na elementach które zostały już utworzone na stronie.

Szczerze mówiąc nie wiem jak to działa dokładnie, ale dane są trwale powiązane z elementami, które utworzyliśmy w D3. Jak pierwszy raz to zobaczyłem to mi szczęka opadała.

Poniższa funkcja pobierze wszystkie elementy “li” czyli punkty listy.  W zależności od wieku osoby dodamy pogrubioną czcionkę do zaznaczonego elementu. Nie odwołuje się w żaden sposób do zmiennej globalnej przetrzymującej dane.

function changeList() {
    d3.selectAll("li")
        .style("font-weight", function (d) {

            if (d.age < 21) {
                return "normal";
            } else {
                return "bold";
            }
    })
}

createAList(json);
changeList();

To jednak dopiero początek. Mając ten prosty tutorial za sobą przejdźmy do utworzenia prostego wykresu słupkowego.

Tworzenie prostego wykresu

Nie ma tutaj żadnej fizyki kwantowej. Funkcja rysowania wygląda prawie tak samo jak wcześniej.

var json = [
{ "name": "Cezary", "age": 25 },
{ "name": "Franek", "age": 11 },
{ "name": "Bartek", "age": 29 },
];

function draw(data) {
    d3.select("body")
    .append("div")
    .attr("class", "chart")
    .selectAll(".bar")
    .data(data)
    .enter()
    .append("div")
    .attr("class", "bar")
    .style("width", function (d) { return d.age * 10  + "px" })
    .style("outline", "1px solid black")
    .text(function (d) { return d.name + ' ' + d.age  });
}

draw(json);

Tworzymy w tej funkcji elementy div, które mają odpowiednią długość w zależności od wieku.

Wiek razy 10 pikseli.

Do utworzonych elementów dodajemy odpowiednie klasy CSS. Przydadzą  się one gdyż obecnie wykres wygląda tak.

Wykres D3.js

Do strony HTML dodajmy więc proste style które pokolorują nasz wykres.

<head>
    <title>Zabawa z D3</title>
    <script src="Scripts/d3.v3.min.js"></script>
<style>
    div.chart {
        font-family: sans-serif;
        font-size: 0.7em;
    }

    div.bar {
        background-color: darkgreen;
        color: white;
        height: 3em;
        line-height: 3em;
        padding-right: 1em;
        margin-bottom: 10px;
        text-align: right;
    }
</style>
</head>

Wykres jako całość ma klasę “chart”. W niej możemy przykładowo dodać odpowiednią czcionkę do zawartości tekstowych.

Styl wykresu jest zależny od kaskadowych styli. Oznacza to, że możesz dopasować wykres do swoich potrzeb nie modyfikując kod JavaScript który go narysował.

Wykres D3.js z CSS

To by było na tyle. D3 nie jest skomplikowany w użyciu. Rysowanie danych wymaga od nas własnej pomysłowości.

Pobierz kod