poniedziałek, 28 listopada 2011

Malowanie wykresów za pomocą JFreeChart cz. 1


Jakiś czas temu „pracując” przy małym projekciku, malowałem sobie ciekawe rysuneczki w po ekranie. Mianowicie bawiłem się biblioteka JFreeChart, która to potrafi w całkiem przystępny sposób malować wykresy w Javie.

Na początku, co bardzo się rzuca w oczy, to ciekawy sposób dystrybucji (czy też modelu biznesowego) tejże biblioteki. Jest ona w zupełności darmowa („is a free 100%”), sa Javadoc’i, jest aplikacja pokazująca możliwości biblioteki, ale chcąc poczytać The JFreeChart Developer Guide trzeba zapłacić. Bardzo ciekawe rozwiązanie, zwłaszcza, że udostępniane Javadoc’i są trochę ułomne.

Na szczęście jest kilka przykładów w Necie np. na stronie java2s.com. Po za tym ten kto jest kumaty, potrafi sobie zdekompilować udostępniane sample i zobaczyć co siedzi w środku :D Czyli dostać to samo co nam daje nam płatny developer guide.

Na początek zróbmy więc coś prostego. Załóżmy, że chcemy zrobić wykres słupkowy (ang. Bar Chart). Utwórzmy więc najpierw obiekt przechowujący nasze dane:

DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset();

Wszystkie zbiory danych (ang. dataset) są reprezentowane za pomocą klas implementujących interfejs Dataset. Interfejs ten jest bardzo ogólny i nie korzystamy z niego bezpośrednio. Chcąc stworzyć pewien wykres, musimy się dowiedzieć, jakiego typu zbioru danych on wymaga. I tak wykres słupkowy, potrzebuje IntervalXYDataset. Za pomocą tego interfejsu, klasa tworząca wykres, będzie wiedziała, jak się odwołać do danych w nim zawartych. Interfejs nie definiuje jednak sposobu dodawania tych danych do zbioru, dlatego musimy skorzystać z konkretnej implementacji przy deklaracji (po lewej stronie znaku równości) tej zmiennej.

Następnie przydało by się wprowadzić jakieś dane do zbioru:

double[][] series01 = new double[][] {{2}, {1}, {3}, {7}, {4}, {5}};
dataset.addSeries("Series 1", series01);

W pierwszej linijce tworzymy tablicę, która musi posiadać 6 wierszy, każdy o takiej samej długości, np. o długości jeden. Jeśli będziemy mieć inną liczbę wierszy otrzymamy IllegalArgumentException z jasnym komunikatem: The 'data' array must have length == 6. Wiersze oznaczają kolejno: x, start-x, end-x, y, start-y i end-y. Nasz wykres korzysta tylko z pierwszych 4rech. X oznacza wartość, dla jakiej definiujemy „wysokość” słupka. Wartość ta może być wyświetlana, gdy najedziemy myszką na ten słupek. Następnie start-x i end-x oznaczają, gdzie ma się zaczynać i kończyć słupek na osi X. Aby wykres był czytelny, wartość x powinna być pomiędzy wartościami start-x i end-x, najlepiej w połowie. Y oznacza wartość, jak wysoki ma być słupek, a start-y i end-y nie są używane, gdyż słupki z założenia na tym wykresie są prostokątne, a nie trapezowate.

Gdybyśmy chcieli wyświetlić kilka słupków dla jednej serii, to wówczas nasze wiersze w tablicy będą dłuższe, ale każdy wiersz musi zawierać dokładnie taką samą ilość liczb.

W drugiej linijce powyższego listingu dodajemy serię do naszego zbioru danych. Należy pamiętać aby użyć unikatowego identyfikatora w ramach zbioru, bo inaczej zobaczymy tylko najnowsze / nadpisane serie.

Następnie tworzymy wykres. Najłatwiej na początku skorzystać z metody fabrykującej dostarczanej przez ChartFactory:

JFreeChart chart = ChartFactory.createXYBarChart("title", "x Label", 
        false, "y Label", dataset, PlotOrientation.VERTICAL, true, 
        true, false);

Jako argumenty podajemy m.in. tytuł wykresu, podpisy osi, zbiór danych, orientację wykresu i jeszcze kilka flag. Trzeci argument mówi czy osi X ma być skalą związana z datami. My takiej nie chcemy. Trzy ostatnie argumenty definiują nam kolejno, czy chcemy legendę, podpowiedzi i adresy URL.

Na koniec trzeba jeszcze stworzyć instancję klasy ChartPanel opakowującą nasz wykres. Dzięki tej klasie możemy już wykres umieścić w JPanel’u Swingowym. Całościowy przykładowy kod i efekt poniżej:

DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset();
double[][] series01 = new double[][] {{2, 22}, {1, 21}, {3, 23}, 
        {7, 27}, {4, 24}, {5, 25}};
double[][] series02 = new double[][]{{5}, {4}, {8}, {13}, {20}, {25}};
dataset.addSeries("Series 1", series01);
dataset.addSeries("Series 2", series02);

JFreeChart chart = ChartFactory.createXYBarChart("title", "x Label", 
        false, "y Label", dataset, PlotOrientation.VERTICAL, true,
        true, false);

JPanel jPanel = new ChartPanel(chart);



Podobne demo, opisujące jak namalować wykres słupkowy możecie zobaczyć na stronie JFreeChart: XY Bar Chart Demo na http://www.java2s.com

Dobra to tyle na początek, przejdźmy do bardziej zaawansowanych zagadnień. Za zadanie miałem stworzyć dość nietypowy wykres, coś takiego:



Kolor danego „kwadracika” był jakoś tam obliczany dla danego X i Y – nie jest to przedmiotem tego wpisu. Mianowicie im wartość Z (przypisana danemu X i Y) mniejsza, tym kolor „zimniejszy”. Spróbujmy więc co podobnego namalować:

DefaultXYZDataset xyzDataset = new DefaultXYZDataset();
double[][] series01 = new double[][]{{1}, {1}, {5}};
double[][] series02 = new double[][]{{2, 5}, {3, 4}, {8, 9}};
xyzDataset.addSeries("Series01", series01);
xyzDataset.addSeries("Series02", series02);

NumberAxis domainAxis = new NumberAxis("X Label");
domainAxis.setRange(0, 10);

NumberAxis valueAxis = new NumberAxis("Y Label");
valueAxis.setRange(0, 7);

PaintScale scale = new GrayPaintScale(0, 10);
XYBlockRenderer renderer = new XYBlockRenderer();
renderer.setPaintScale(scale);

XYPlot plot = new XYPlot(xyzDataset, domainAxis, valueAxis, renderer);

JFreeChart chart = new JFreeChart("title", plot);

JPanel jPanel = new ChartPanel(chart);

Na początku tworzymy zbiór danych typu DefaultXYZDataset. Oznacza to, że nasze dane składają się z 3ch współczynników, co też widać w liniach 2 i 3. Następnie dodajemy nasze serie danych do zbioru. W kolejnych linijkach definiujemy nasze osie - jakie będą miały nazwy i zakres.

W linii 13 tworzymy obiekt typu PaintScale. Klasa ta jest odpowiedzialna za odpowiednie pokolorowanie „kwadracików” na wykresie. W przykładzie korzystam z przykładowej implementacji GrayPaintScale, która tworzy skalę szarości. Jak malować na kolorowo zostawiam jako zadanie domowe :P

Następnie definiujemy Renderer‘a a dokładniej XYBlockRenderer. Obiekt ten jest odpowiedzialny za odpowiednie wymalowanie danych na wykresie. Chcąc skorzystać ze wcześniej utworzonej skali, musimy ją przekazać za pomocą metody setPaintScale().

Następnie musimy spiąć razem zbiór danych, osie wykresu i obiekt renderujący w coś co się nazywa Plot, a w naszym przypadku XYPlot. Jest to klasa do której JFreeChart deleguje malowanie osi i danych. Mając już wspomniany XYPlot, możemy w końcu utworzyć nasz wykres (linia 19) i odpowiadający mu Panel (linia 21).

Poniżej efekt jaki możemy osiągnąć, wykonując poniższy kod:



Umiemy już namalować szare kwadraciki na wykresie, a stąd już niedaleko do żądanego efektu. Jak ktoś chce zobaczyć końcowy efekt to zapraszam do repozytorium MFCCChart na bitbucket.org

W następnym wpisie opiszę jeszcze jak namalować skalę kolorów.

Brak komentarzy:

Prześlij komentarz