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.

niedziela, 20 listopada 2011

Problem sponsoringu konferencji


Wielu znajomych (i tych mniej znajomych) pyta mnie czasem o to, czy skoro tak licznie jeżdżę po konferencjach Javowych (w tym roku już 4 a to jeszcze nie koniec), to czy są to wyjazdy sponsorowane przez kogoś (pracodawcę / babcię), czy po prostu sam sobie je opłacam?

Otóż różnie to z tym bywa: czasem jadę z ramienia pracodawcy, a czasem za swoje (nie za mszowe) pieniądze. Uzasadniając takie postępowanie, podeprę się słowami Wujka Boba zapisanymi w książce The Clean Coder [str. 16].

“Your career is your responsibility. […]It is not your employer’s responsibility to train you, or to send you to conferences, or to buy you books. These things are your responsibility. Woe to the software developer who entrusts his career to his employer.”

Nie jest zadaniem / odpowiedzialnością pracodawcy płacić za Twój rozwój! Pracodawca daje Ci pracę, kupuje twoją dostępność, twoją wiedzę i umiejętności, dając Ci w zamian co miesiąc wypłatę przelewem na konto. Pracodawca „kradnie” z Twojego życia 40 godzin tygodniowo, oczekując w zamian rezultatów: skończonego, działającego projektu, udanego wdrożenia, zadowolenia klienta…

Pracodawca chce, aby u niego pracowali tylko najlepsi, ale najlepiej nie ponosząc przy tym żadnych kosztów związanych ze szkoleniami, wyjazdami na konferencje, czy z chociażby kupnem książki. Po prostu rozwój pracowników nie jest głównym celem / zadaniem pracodawcy. Od tego są uczelnie wyższe i instytucje prowadzące szkolenia. Te pierwsze mogą być bezpłatne (zależnie od państwa), a za szkolenia trzeba już sobie płacić.

Rozumiejąc taką politykę firm, powinniśmy tym bardziej doceniać swojego pracodawcę, jeśli ten wspiera nasz rozwój. Nie możemy jednak zapominać, że to My bierzemy odpowiedzialność za nasz rozwój i że to My musimy znajdować czas w swoim życiu, aby przeznaczyć go na swój rozwój.

A ile czasu powinniśmy poświęcać na samodoskonalenie?

Wujek Bob w przytaczanym fragmencie jego książki pisze o takim podziale tygodnia:

  • 40 h pracy dla pracodawcy (według ustawy)
  • 20 h pracy dla siebie (samodoskonalenie)
  • 56 h na sen (8h dziennie) 

No i jeszcze zostaje 52 h na wszystko inne: rodzina, przyjaciele, znajomi, aktywność fizyczna, odpoczynek. Myślę, że jest to bardzo optymistyczne oszacowanie. Wiadomo, nie jesteśmy robotami i możemy czasem mieć gorszy dzień lub tydzień, kiedy nic się nie chce. Ja te „zalecane” 20 godzin traktuję, jako oszacowanie, ile mniej więcej powinienem w tygodniu robić coś pożytecznego dla siebie. Czy osiągam te 20 godzin – nie wiem, nie mierzyłem tego.

A co robić w tym czasie?

No cóż, możliwości jest sporo i można o tym napisać osobny artykuł. Uczestnictwo w konferencjach / warsztatach branżowych jak najbardziej się zalicza do tych aktywności. Konferencje są różne i różne są ich poziomy, ale zawsze można się czegoś nauczyć. Nawet jak idziemy na prezentację, która nie do końca jest związana z naszym obszarem zainteresowań, możemy się dowiedzieć, że w ogóle coś takiego jest, jak działa i jak używać. A gdy mamy poznać nową technologię, to lepiej posłuchać kogoś kto miał z tym styczność, niż samemu rozkminiać tutoriale. Sam siłę i potęgę wykładów (chociażby studenckich) doceniłem dopiero na 4tym roku studiów, jak w końcu były one w miarę konkretne i gdy ktoś sensowny je prowadził.

Inną zaletą, jakie dają nam konferencje to możliwość pogadania i poznania ludzi z tymi samymi zainteresowaniami, co za sobą niesie wymianę doświadczeń, opinii itp. Inną bardzo miłą kwestią są wszelkie towarzyszące konkursy z nagrodami. Jak się już nie raz przekonałem, w cale nie jest tak ciężko coś wygrać (myszka Logitech’a na 4developers 2010, zniżki na zakupy w Helionie na Javarsovi 2010, pendrive'y i bilety do kina na Confiturze 2011). Ostatni konkurs po Warsjawie 2011 był bardzo fajny. Do rozlosowania były 2 książki ufundowane przez firmę TouK dla osób, które napiszą relację na blogu z konferencji. No i jedna z książek (Czysty Kod) przypadła mi, co pokazuje, że prowadzenie bloga może być przydatne ;)

Tak więc jak by ktoś jeszcze w tym roku chciał porobić coś ciekawego, to 3ciego grudnia jest Global Day of Coderetreat, czyli w ponad 80 miastach na świecie odbywać się będą warsztaty CodeRetreat. W Polsce taka impreza będzie w Krakowie na którą oczywiście się wybieram.

Szykuje się jeszcze Agile Development Day w Warszawie na który jeszcze można się próbować zapisać, a także w tym samym czasie Cracow.Mobi w Krakowie. A dla mieszkańców Wrocławia powstaje właśnie: Wrocław Google Technology User Group. Pierwsze spotkanie na temat Chrome Apps odbędzie się 29 listopada 2011 o godz. 19:00.

Prawdziwa gradka szykuje się jednak w pierwszym kwartale przyszłego roku, a mianowicie 33 degree. Ja już się zarejestrowałem, mimo że nie wiem czy sam będę musiał sobie ją opłacić, czy pracodawca mi ją zasponsoruje. W każdym bądź razie proszę Was, drogi czytelniku / czytelniczko, abyście podczas rejestracji podali mój adres email mstachniuk gmail com, dzięki czemu Wy nic nie tracicie, a ja zyskam zniżkę na tę konferencję. Szczegóły promocji: 33rd Degree Buzz - get 20% off.

Do zobaczenia na konferencjach!