Highcharts kütüphanesi ile veri görselleştirme 

Elimizdeki verileri güzel bir şekilde görselleştirmediğimiz sürece bu verilerden anlamlı sonuçlar elde etmek oldukça güç. Bilhassa üst yönetici panellerinde (dashboard) anlamlı ve göze hoş gelen raporlar üretmemiz gerekiyor. Bilgiyi görselleştirerek onu anlamlı bir hale getiriyoruz aslında bundan da önemlisi bilgiye odaklanabiliyoruz. Danışmanlık hizmeti verdiğimiz kurumda, kurum başkanının yapılan sunumları bir türlü beğenmediği şikayetini çalışma arkadaşlarımdan işitiyordum. Şüphesiz beğenmeyişinin bir çok haklı veya haksız sebebi vardır ama benim en çok dikkatimi çeken nokta başkanın bir sürü yazıdan ve tablodan hazırlanmış “bilgi bombardımanı” sunumlarını eleştirmesi olmuştu. Geçenlerde dinlediğim bir Ted Talk sunumunda anlatıcı veriyi toprağa benzetmişti bu verilerden sağlanan grafikleri/motifleri de toprağın üzerinde açan çiçeklere. Boş ve çorak uçsuz bucaksız bir manzara sadece Nuri Bilge Ceylan’ın filmlerinde güzel, dijital dünyanın peyzaj mimarlarına düşen olabildiğince anlamlı ve okunur raporlar üretmek.

Son zamanlarda vaktimin büyük bir kısmını highcharts.js kütüphanesi ile geçiriyorum. Alternatiflerine göre görselliği ,kullanımı ve dokümantasyonu oldukça iyi. Flash kullanımının popülaritesini yitirmeye başladığı zamanlarda javascript ile hazırlanan sayfaların bir türlü istenilen görselliğe ulaşamadığı dönemleri hatırlıyorum. Özellikle rapor sayfalarında flash ile hazırlanan sayfalar çok daha albenili oluyordu. Geliştiricilerin artan javascript eğilimlerininde etkisiyle flash sayfalarının görselliğini çoktan aşmış durumdayız. Raporlarma için kullanılan highcharts.js kütüphanesi ile pasta, çubuk, sutun, çizelge vs. gibi raporlar üretmek mümkün. Aynı zamanda highcharts ‘ın dışa aktarma sunucusunu veya kendi özel dışa aktarma sunucumuzu kullanarak (özel dışa aktarma sunucusunu hazırlamayı ayrıca yazacağım) JPEG,PNG,SVG ve PDF çıktılar üretebiliyoruz, istediğimiz kadar detaya inebiliyoruz (Drill-Down). Bu yazıda highcharts ekibinin hazırlamış olduğu highmap.js ürününü kullanarak, hazırladığım harita üzerinde veriyi nasıl görselleştirdiğimi anlatacağım.

 

Örnek tamamlandığında sonuç olarak aşağıdaki gibi soldaki haritadan seçtiğimiz ilçenin oy dağılımını sağdaki pasta grafikte gösteren bir internet sayfası yapmış olacağız.

Bu örnekte kullandığım dil ve teknolojiler:

Java 7,

Tomcat 7.XX

MongoDB 2.10.1,

highcharts.js XX,

highmap.js XX,

Inkscape 0.48 (Harita hazırlamak için)

Hazırladığım Karabük (kolay olması için Karabük’ü seçtim) haritası üzerinde oy oranlarını rastgele belirlediğim siyasi partilerin ilçelere göre oy dağılımlarını göstereceğim. İlk iş Karabük haritasını oluşturmak. Bunu yapabilmek için ücretsiz Inkscape programını kullanabiliriz. Inkscape ile basitçe arkaplana koyduğumuz bir PNG’nin highmap.js kütüphanesinin kullanabileceği formata dönüştürmek için kullanacağım.

1.Arkaplan olarak belirlediğim PNG’yi Inkscape programına sürüklüyoruz. Ben Vikipedi’nin Karabük sayfasındaki şu resmi kullanacağım.

2.Sol araçlar bölmesindeki “Draw bezier curves and straight lines”(Shift + F6) aracını kullanarak Yenice ilçesinin sınırlarını çizdim. Photoshop programındaki çokgen kement aracına benzer kullanımı var.Burada ne kadar detaylı çalışırsak oluşturacağımız haritanın da gerçekçiliğini artırmış olacağımızı unutmamalıyız.

3.İlçe sınırlarını çizdikten sonra crtl+shift+F tuş kombinasyonunu kullanarak “Fill and Stroke” panelini açtım burada “Flat color” seçeneğini işaretleyip rengi kırmızı olarak belirledim.Böylelikle çevresini belirlediğimiz ilçenin alanınıda belirlemiş olduk.

4.Ctrl+Shift+O tuş kombinasyonu ile Object Property panelini açıp çizdiğimiz alanın id ve title gibi bilgilerini yazıyoruz. Burada set tuşunu kullanmazsak yazdığımız bilgilerin kaydedilmediğine dikkat edin. Bunu yapmazsak highmap.js’nin tıklama fonksiyonunu doğru bir şekilde kullanamayız.

5.Benzer şekilde diğer ilçelerin sınırlarını belirliyoruz.Burada yazıyı uzatmamak adına birkaç detayı atlıyorum ilgili kaynak makaleyi okuduğunuz takdirde diğer detayları da görebilirsiniz.

6.Çizim işlemini tamamladıktan sonra çalışmamızı SVG olarak kaydediyoruz.

7.SVG çalışmamızı test etmek için internet tarayıcısında görüntüleyebiliriz, çalışmamızı bir metin editöründe (ben notepad++ kullandım) açıp bütün metini kopyalıyoruz.

8.Kopyaladığımız metini www.highcharts.com/studies/map-from-svg.htm sayfasındaki “Online SVG URL or SVG markup:” bölününe yapıştırmamız gerekiyor buraya kadar her şey yolunda ise “Highcharts maps from SVG sources” bölümünde çalışmamız görüntülenecektir.

9.Sol altta “View data” bölümüne tıklarsak yaptığımız çalışmanın JSON tipindeki karşılığını görüntüleyebiliriz. Bu highmap.js kütüphanesinde kullanacağımız “series” değişkenine karşılık gelen veridir. bu verileri http://jsfiddle.net/highcharts/TUy7x/linkine gidip series değişkenine yapıştırmamız yeterli olacaktır. Sonuç olarak elde ettiğimiz veri http://jsfiddle.net/ibrahimbaykal/TUy7x/299/ olacaktır.

Buraya kadar olan kısımda bir sorun olmadıysa artık üzerine tıklanabilir bir Karabük ilçeleri haritası hazırlamış bulunmaktayız. Birinci bölüm de bir nevi çeviri yapmış oldum ilgili orjinal makaleye http://www.highcharts.com/docs/maps/custom-maps linkinden erişebilirsiniz.

MongoDB’ye komut satırını kullanarak verileri aşağıdaki kodlamayla 6 ilçe için yazdım. Böylelikle MongoDB(Veritabanı (database) “bulk” koleksiyon (collection) “test”) tarafını hazırlamış oldum.

db.test.insert([
{party: 'AP',vote: 40,county: 'Safranbolu'},
{party: 'BP',vote: 40,county: 'Safranbolu'},
{party: 'CP',vote: 20,county: 'Safranbolu'},
{party: 'AP',vote: 10,county: 'Ovacık'},
{party: 'BP',vote: 20,county: 'Ovacık'},
{party: 'CP',vote: 70,county: 'Ovacık'},
{party: 'AP',vote: 30,county: 'Eskipazar'},
{ party: 'BP',vote: 60,county: 'Eskipazar'},
{party: 'CP',vote: 10,county: 'Eskipazar'},
{party: 'AP',vote: 30,county: 'Eflani'},
{party: 'BP',vote: 40,county: 'Eflani'},
{party: 'CP',vote: 30,county: 'Eflani'},
{party: 'AP',vote: 20,county: 'Yenice'},
{party: 'BP',vote: 30,county: 'Yenice'},
{party: 'CP',vote: 50,county: 'Yenice'},
{party: 'AP',vote: 80,county: 'KarabükMerkez'},
{party: 'AP',vote: 10,county: 'KarabükMerkez'},
{party: 'AP',vote: 10,county: 'KarabükMerkez'}
])

 

Sonuç olarak mongoDB veritabanında aşağıdaki gibi kayıtlarınız olmalı.

MongoDB ve JSON kullanacağım için java projeme mongo-2.10.1 ve gson-2.2.4 kütüphanelerini ekledim. Örnek projemi çok fazla uzatmak istemediğimden bir tane sunucu tarafı (servlet CountyDetail) bir tane veritabanı erişim sınıfı (MongoDBManager) bir tane de model sınıfı (ElectionResult) yazdım. Sonuç olarak aşağıdaki proje yapısını oluşturdum.

Sunucu İstek Sınıfı

İnternet sayfasından (istemci) yapılan istekleri karşılayan sınftır. Bu sınıfta yazdığım doPost metodunda gelen istekteki “op” parametresine bakılarak hangi metodların kullanılacağı ve hangi parametrelerin alınacağı belirlenmektedir.Benim projemde şayet “op” parametresi “countydetail” ise “countyID” parametresi belirlenip getCountyDetail metodu çağırılmaktadır.

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String op = request.getParameter("op");
    switch (op) {
    case "countydetail":
        String countyID = request.getParameter("countyID");
        getCountyDetail(response,countyID);
        break;
        // diğer operasyonlar buraya tanımlanabilir
    default:
        // ...
        break;
    }
}

“countydetail” metodunda veri erişim sınıfındaki hangi metodun kullanılacağı belirlenmektedir.

private void getCountyDetail(HttpServletResponse response, String countyID) {
    List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
    MongoDBManager mongoDB = new MongoDBManager();
    list = mongoDB.getCountyResult(countyID);
    write(response,list);
}

“write” metoudu ise istemci tarafına gidecek bilginin ne olacağı ve nasıl gönderileceği bilgisinin bulduğu metottur. Yazdığım metotta istemciye bir UTF-8 karakter setinde yazılmış JSON gönderilmesi sağlanmaktır.

private void write(HttpServletResponse response, List<Map<String, Object>> list) {
    response.setContentType("application/json");
    response.setCharacterEncoding("UTF-8");
    try {
        response.getWriter().write(new Gson().toJson(list));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
Sunucu istek sınıfının tüm hali:
package com.highcharts.Servlets;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.gson.Gson;
import com.highcharts.MongoDBUtils.MongoDBManager;

/**
 * Servlet implementation class CountyDetail
 */
@WebServlet("/countydetail")
public class CountyDetail extends HttpServlet {
    private static final long serialVersionUID = 1L;
        
    /**
     * @see HttpServlet#HttpServlet()
     */
    public CountyDetail() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String op = request.getParameter("op");
        switch (op) {
        case "countydetail":
            String countyID = request.getParameter("countyID");
            getCountyDetail(response,countyID);
            break;
            // diğer operasyonlar buraya tanımlanabilir
        default:
            // ...
            break;
        }
    }

    private void getCountyDetail(HttpServletResponse response, String countyID) {
        List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
        MongoDBManager mongoDB = new MongoDBManager();
        list = mongoDB.getCountyResult(countyID);
        write(response,list);
    }

    private void write(HttpServletResponse response, List<Map<String, Object>> list) {
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        try {
            response.getWriter().write(new Gson().toJson(list));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

 

Veritabanı Erişim Sınıfını 

Basitçe mongoDB ‘ye bağlanıp “bulk” veritabanında “test” koleksiyonundaki (collection) ilgili kayıtı çeken sınıfı aşağıdaki gibi kodladım. MongoDBManager sınıfında mongoDB erişim bilgileri bulunmaktadır. Bu sınıfın getCountyResult(String countyID) metodu istek yapılan ilçenin oy dağılımını veritabanından çeken yordamdır parametre olarak ilçe kimlik bilgisi almaktadır.

package com.highcharts.MongoDBUtils;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.highcharts.Model.ElectionResult;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;

public class MongoDBManager {
     
    MongoClient mongoclient;
    DB db;
    DBCollection collection;

    public MongoDBManager() {
        try {
            mongoclient = new MongoClient("localhost" , 27017);
            db                   = mongoclient.getDB("bulk");
            collection = db.getCollection("test");
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
     
    public List<Map<String, Object>> getCountyResult(String countyID){
        List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
        BasicDBObject nfield = new BasicDBObject();
        nfield.put("_id", 0); // _id bilgisi gelmesin
        BasicDBObject query = new BasicDBObject().append("county", countyID); // ilçe bilgisi veriliyor
        DBCursor cursor = collection.find(query,nfield);
        while(cursor.hasNext()){
            DBObject dbo = cursor.next();
            BasicDBObject basicdbobj = (BasicDBObject) dbo;
             
            ElectionResult ero = new ElectionResult();
            ero.setParty(basicdbobj.getString("party"));
            ero.setVote(basicdbobj.getInt("vote"));
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("electionresult", ero);
            list.add(map);
        }
        return list;
    }      
}

 

Model Sınıfı 

Bu sınıf “ElectionResult” nesnesinin tanımlarının yapıldığı ve metotlarının belirlendiği basit java sınıfıdır. Bu sınıfın iki değişkeni vardır, “party” siyasi parti ismine “vote” oy miktarına karşılık gelmektedir.

package com.highcharts.Model;

public class ElectionResult {

    String party;
    int vote;

    public String getParty() {
        return party;
    }

    public void setParty(String party) {
        this.party = party;
    }

    public int getVote() {
        return vote;
    }

    public void setVote(int vote) {
        this.vote = vote;
    }
     
}

 

Javascript Dosyası

Bu bölümde daha çok javascript tarafını irdeleyeceğim. Birinci bölümde hazırladığımız javascript kodlarına ilaveler yaparak kullanışlı bir hale getirmeye çalışacağım. Birinci bölümde anlattığım data tarafıyla çok fazla ilgilenmeyeceğim.

Birinci bölümde hazırladığımız grafiği anlamlı bir hale getirebilmek için aşağıdaki kodları kullandım. Burada “title” ,”subtitle” başlık ve altbaşlık belirlemek için, “credits” sağ alt köşedeki “highcharts.com” bağlantısını yok etmek için , “tooltip” imleç ile ilçe üzerine gelindiğinde bilgi kutucuğunun çıkmasını önlemek için son olarak “legend” grafik altındaki açıklamaları/göstergeleri kapatmak için kullanılmıştır.

$('#container')
    .highcharts(
    'Map', {
    title: {
        text: "Karabük İli Seçim Sonuçları"
    },
    subtitle: {
        text: "İlçeler"
    },
    credits: {
        enabled: false
    },
    tooltip: {
        enabled: false
    },
    legend: {
        enabled: false
    },

...

“datalabels” seçeneğinde her bir ilçe parçasının üzerindeki yazıların özelliklerini (rengi,ne yazacağı ,görünürlük) belirledim. “events” seçeneği ile olay yönetimi yapılmaktadır. Örneğimde sadece tıklama olayı olduğu için “click” seçeneğini kullandım.

series: [{
            type: "map",
            dataLabels: {
                enabled: true,
                color: 'white',
                format: '{point.name}'
            },
            events: {
                click: function (e) {
                    var name = e.point.name;
                    getCountyDetail(name);
                }
            },
            data: [{ ...
İlçe parçasına tıklandığında sunucudan istek yapmak için aşağıdaki kod parçalarını kullandım.
function getCountyDetail(name) {
    $.ajax({
        url: "countydetail",
        type: "POST",
        datatype: "JSON",
        data: "op=countydetail&countyID=" + name,
        success: successGetCountyDetail
    });
}

Yukarıdaki istek başarılı bir şekilde sonuçlanırsa “successGetCountyDetail” fonksiyonu çalışacaktır. Burada 1. satır ile 9. satır arasındaki kodları birinci bölüm geri kalan kısım ise ikinci bölüm olarak düşünebiliriz. Birinci bölümde sunucu tarafından gelen veriyi highcharts.js kütüphanesinin kullanabileceği formata dönüştürmekle uğraşıyoruz. “name” değişkeni veri adına ,”y” değişkenide bu verinin sayısal değerine karşılık gelmektedir. Highcharts’da pasta grefik için bu şekilde(name,y) bir kullanım formatına gidilmiştir diğer grafik tipleri içinde ayrı formatlar bulunmaktadır. İkinci kısımda ise oluşturulacak pasta grafiğin özellikleri yukarıda harita kısmında anlattığıma benzer bir şekilde belirlenmektedir. Burada pasta grafiğin kullanacağı verinin belirlendiği kısım 45. satırdaki “data: _data” ifadesidir.

function successGetCountyDetail(data){
    var _data = new Array();
     var limit = data.length;
     for(var i =0;i<limit;i++){
         var ero = new Object();
         ero.name= data[i].electionresult.party;
         ero.y= data[i].electionresult.vote;
         _data.push(ero);
     }
     
    $('#piecontainer').highcharts({
        chart: {
            plotBackgroundColor: null,
            plotBorderWidth: null,//1,
            plotShadow: false
        },
        title: {
            text: 'Siyasi parti Oy Dağılımları'
        },
        subtitle:{
            text:"Karabük ili genel dağılım"
        },
        tooltip: {
            pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
        },
        credits:{
            enabled:false
        },
        plotOptions: {
            pie: {
                allowPointSelect: true,
                cursor: 'pointer',
                dataLabels: {
                    enabled: true,
                    format: '<b>{point.name}</b>: {point.percentage:.1f} %',
                    style: {
                        color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black'
                    }
                }
            }
        },
        series: [{
            type: 'pie',
            name: 'Oy Oranı',
            data: _data
        }]
    });
     
}

 

javascript kodlarının yüm hali:

function onload() {
    // Initiate the chart
    $('#container')
            .highcharts(
                    'Map',
                    {
                        title : {
                            text : "Karabük İli Seçim Sonuçları"
                        },
                        subtitle : {
                            text : "İlçeler"
                        },
                        credits : {
                            enabled : false
                        },
                        tooltip : {
                            enabled : false
                        },
                        legend : {
                            enabled : false
                        },
                        series : [ {
                            type : "map",
                            dataLabels : {
                                enabled : true,
                                color : 'white',
                                format : '{point.name}'
                            },

                            name : 'Karabük Oy Dağılımı',
                            events : {
                                click : function(e) {
                                    var name = e.point.name;
                                    getCountyDetail(name);
                                }
                            },
                            point : {
                                events : {

                                }
                            },
                            data : [
                                    {
                                        name : "Yenice",
                                        value : 100,
                                        path : "M0,-375,59,-473,138,-521,270,-557,317,-574,320,-549,295,-504,211,-366,169,-327,197,-268,191,-217,0,-251z"
                                    },
                                    {
                                        name : "Safranbolu",
                                        value : 25,
                                        path : "M320,-571,528,-692,565,-743,581,-827,621,-863,669,-880,719,-936,756,-883,711,-832,677,-793,677,-757,694,-717,750,-672,764,-653,767,-619,837,-577,843,-484,806,-442,697,-428,666,-459,567,-481,537,-563,497,-566,452,-560,419,-538,357,-476,295,-490,320,-549z"
                                    },
                                    {
                                        name : "Eflani",
                                        path : "M719,-934,781,-939,848,-976,874,-959,958,-782,938,-726,865,-672,837,-619,837,-580,775,-625,761,-664,694,-720,683,-757,674,-796,756,-886z"
                                    },
                                    {
                                        name : "Eskipazar",
                                        path : "M194,-265,191,-144,399,-43,441,-18,604,-167,761,-189,697,-268,685,-243,638,-243,562,-276,452,-254,376,-299,354,-293,292,-251,233,-257z"
                                    },
                                    {
                                        name : "Ovacık",
                                        path : "M694,-420,694,-262,750,-186,955,-254,1000,-344,890,-467,840,-495,806,-439,716,-434z"
                                    },
                                    {
                                        name : "Karabük Merkez",
                                        path : "M289,-493,354,-470,427,-549,472,-560,531,-563,565,-476,663,-459,699,-422,697,-259,680,-245,638,-251,576,-282,461,-257,388,-307,351,-293,289,-251,199,-268,166,-335,222,-375z"
                                    } ]
                        } ]
                    });
}

function getCountyDetail(name) {
    $.ajax({
        url : "countydetail",
        type:"POST",
        datatype:"JSON",
        data:"op=countydetail&countyID="+name,
        success:successGetCountyDetail
    });
}

function successGetCountyDetail(data){
    var _data = new Array();
     var limit = data.length;
     for(var i =0;i<limit;i++){
         var ero = new Object();
         ero.name= data[i].electionresult.party;
         ero.y= data[i].electionresult.vote;
         _data.push(ero);
     }
     
    $('#piecontainer').highcharts({
        chart: {
            plotBackgroundColor: null,
            plotBorderWidth: null,//1,
            plotShadow: false
        },
        title: {
            text: 'Siyasi parti Oy Dağılımları'
        },
        subtitle:{
            text:"Karabük ili genel dağılım"
        },
        tooltip: {
            pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
        },
        credits:{
            enabled:false
        },
        plotOptions: {
            pie: {
                allowPointSelect: true,
                cursor: 'pointer',
                dataLabels: {
                    enabled: true,
                    format: '<b>{point.name}</b>: {point.percentage:.1f} %',
                    style: {
                        color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black'
                    }
                }
            }
        },
        series: [{
            type: 'pie',
            name: 'Oy Oranı',
            data: _data
        }]
    });
     
}

 

HTML kodları :

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/maps/modules/map.js"></script>
<script src="js/index.js"></script>
    
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style>
.wrapper {
    height: 500px;
    width: 500px;
    margin-left:100px;
    float:left;
}
.loading {
    margin-top: 10em;
    text-align: center;
    color: gray;
}
</style>
</head>
<body onload="onload();  pieloader()">
<div id="container" class="wrapper"></div>
<div id="piecontainer" class="wrapper"></div>
</body>
</html>

 

Örneğin tamamı : https://github.com/ibrahimbaykal/highcgarts

 

No Comments

Post a Comment

Comment
Name
Email
Website